这几天享学课堂alvin老师讲WMS机制,有幸听了,感触颇深,所以回头写了这篇文章,进行知识点的记录和回顾。
想要搞清楚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是在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);
- ...
- }
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上面的。
上面两个对象的创建都是在需要其的时候创建的,那么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中的方法开始的,具体后续流程,我们在下一章里面去讲。