当前位置:网站首页>View绘制流程1-View与Window的关系
View绘制流程1-View与Window的关系
2022-07-26 21:20:00 【失落夏天】
前言:
想要搞清楚View的完整的绘制流程,我认为主要分为三大块需要掌握。
第一块,最终呈现给用户看的其实是Window,那么Window与View的关系是怎样的?主要是搞清楚Window,DecorView,ViewRootImpl,WindowManager的关系。
第二块,了解了各个组件之间的关系,那么我们就可以开始了解一次完整的绘制是执行了怎样的一个流程。绘制流程是如何执行到我们常说的measure,layout,draw的流程上的。
第三块,draw的流程走完了。产生了各种绘制UI的指令,那么这些指令是如何形成数据,发给SurfaceFlinger,最终发送到硬件层进行展示的。
所以围绕着这是三块,写了三篇文章来进行详细的讲解。第一块和第二块主要是java层的讲解,第三块主要是JNI层的讲解。
一.几个比较重要的角色
开始流程介绍之前,我们先介绍一些比较重要的角色:
1.DecorView:这是最顶层的View,继承自ViewGroup。我们经常设置的setContentView等等,都是放到DecorView中的。
2.ViewRootImpl:最顶层的ViewParent,但是并不是一个View。它属于一个管理者,维护DecorView和Window的关系。绘制流程的控制等等,都是由其来维护的。
3.PhoneWindow:Window翻译过来是窗口,它在安卓中的概念就是锁定屏幕上的一块区域进行显示。而PhoneWindow则是用来装载和显示DecorView的,我们activity中setConentView方法最终也会交给PhoneWindow的setConentView来实现。
4.WindowManagerImpl:WindowManagerGlobal的代理类,基本上功能都是交由WindowManagerGlobal处理。
5.WindowManagerGlobal:视图的管理装载类。一个应用中会有很多activity,其实每个activity都会对应一个DecorView,而这些DecorView都会保存在WindowManagerGlobal中。
6.IWindowSession:WindowManageService在客户端的Binder代理类。客户端最终的绘制操作,需要通过binder传递给WindowManageService,然后由其在传递给SurfaceFlinger去完成最终的合成。
二.PhoneWindow,DecorView,ViewRootImpl何时创建的?
2.1 PhoneWindow何时创建?
PhoneWindow是在ActivityThread执行performLaunchActivity方法的时候去创建的。Activity一定要关联window才能显示,所以在activity之初就创建了其所对应的Window。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//反射创建activity
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
appContext.getAttributionSource());
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
...
//activity进行绑定
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken, r.shareableActivityToken);
...
return activity;
}
Activity.attch方法:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
}2.2 DecorView何时创建?
DecorView是在Activity的onCreate方法时创建的,具体是在setContentView的时候。这时候contentView需要找一个父容器进行装载,则恰好是需要创建的DecorView的时候。
Activity的setContentView方法最终会交给PhoneWindow的setContentView方法进行处理。则此时会判断如果mContentParent为空,则进行DecorView的创建。
if (mContentParent == null) {
installDecor();
} installDecor方法中,如果DecorView不存在,则会去创建。
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
...
}创建完成后,再去判断mContentParent是否存在,如果不存在则去创建contentParent,并且把其加入到DecorView中。
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}generateLayout方法中,根据layoutResource反射去创建ViewGroup,然后把其加入到DecorView中。
protected ViewGroup generateLayout(DecorView decor) {
...
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
return contentParent;
}这里生成root对象,其实就是我们常用的contentView,然后加入到DecorView中。DecorView虽然是ViewGroup,但其child也只会有这一个对象。
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
...
final View root = inflater.inflate(layoutResource, null);
...
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
...
}这里稍微扩展一下,我们经常设置的各种主题颜色,其实也是在这个时候进行解析处理的。主题样式其实就是作用于Window上面的。
2.3 ViewRootImpl何时创建?
上面两个对象的创建都是在需要其的时候创建的,那么ViewRootImpl自然也不例外。界面开始渲染其实是在onResume的时候,那么ViewRootImpl的创建就是在onResume的时候。
ActivityThread执行到handleResumeActivity方法的时候,会进行判断,如果ActivityClientRecord中还未绑定window并且没有执行finish方法,则会向WindowManager注册decorView。
这里我们看到判断了finish状态,也就是说如果在onReusme之前如果执行了finish方法,是不会走创建流程的,也不会创建ViewRootImpl,从而避免创建不需要的对象,这就体现了需要时才去创建的合理性。
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
//向WindowManager注册DecorView
wm.addView(decor, l);
}WindowManager的最终实现类是WindowManagerGlobal,其addView方法如下:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
...
//ViewRootImpl绑定DecorView
root.setView(view, wparams, panelParentView, userId);
}
}我们可以看到,会先创建ViewRootImpl,然后把DecorView和root分别加入到集合当中,最终在把DecorView和ViewRootImpl进行绑定。
所以我们可以得出这样一个结论,DecorView和ViewRootImpl是一一对应的。
这里继续扩展一下,问一个曾经的百度面试题,如果我在ActivityB中,想拿到上一个页面ActivityA的View该怎么做呢?聪明的你看到mViews估计就已经知道答案了。
三.后续流程
等几个重要的对象都已经创建并且完成绑定后,则后续就可以执行渲染的流程了。
这个流程也是从onResume中的方法开始的,具体后续流程,我们在下一章里面去讲。
边栏推荐
- Altium designer 22 Chinese character garbled
- 仅需一个依赖给Swagger换上新皮肤,既简单又炫酷
- Oppo self-developed large-scale knowledge map and its application in digital intelligence engineering
- OPPO 自研大规模知识图谱及其在数智工程中的应用
- 同花顺上面开户安全吗,开户怎么选券商
- Altium Designer 22 修改选中元件的层属性
- What to do if the browser home page is tampered with, and how to recover if the home page is tampered with
- 方法重载与方法重写
- 09 expr 命令
- 06 CP command
猜你喜欢

彻底搞通服务发现的原理和实现

Thorough load balancing

JS 延迟执行window.onload

Matlab draw short-term energy diagram

VI and VIM text editors

06 CP command

带你搞懂MySQL隔离级别,两个事务同时操作同一行数据会怎样?

Xshell7 personal free download, use

Altium designer 22 modify the layer properties of the selected component

A new technical director asked me to do an IP territorial function~
随机推荐
LDAP——实现用户统一登录管理
1 - "pytorch deep learning practice" - linear model
Ansible installation and use
Xshell7 personal free download, use
Matlab pitch period estimation post-processing
Thoroughly understand the principle and implementation of service discovery
梦里的一碗面
Flink's real-time data analysis practice in iFLYTEK AI marketing business
JS verify complex password
Pytorch squeeze() unsqueeze() 用法
Technology sharing | do you know the functions of the server interface automated testing and requests library?
Go --- identifiers and keywords in go language
matlab 基音周期估计后处理
45. Instance segmented labelme dataset to coco dataset and coco dataset to labelme dataset
Pytorch--Visdom使用
06 CP command
Selenium automated test interview questions family bucket
Triangular wave spectrum of MATLAB excitation model
Props with type Object/Array must...
A new technical director asked me to do an IP territorial function~