Android RenderActivity窗口构成和绘制解析

Posted 魏长志

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android RenderActivity窗口构成和绘制解析相关的知识,希望对你有一定的参考价值。

阅读者三篇android绘制文章,会让你对理解Android绘制有帮助:


一、Activity窗口构成

这里我们会介绍到ActivityPhoneWindowDecorViewViewRootImplWindowManagerImplWindowManagerGlobalActivityThreadSurface,关于Activity窗口有很多方面可讲,我这里只侧重于Activity窗口的Render方面来讲解。先来两张张大家早已经烂熟于心的图:

1-1 Activity启动流程图

1-2 Activity构成图

一个Activity的构成有,ActivityPhoneWindowDecorView,再加上DecorView里面的TitleBar和我们填充的content内容,这些还Activity构成需要的具体类,不是一些辅助类,其实还有ViewRootImplWindowManagerImplWindowManagerGlobalActivityThread这几个不可见的辅助类,虽然是Activity构成可见UI界面没用到,但是这几个辅助类都是直接或者间接负责了Activity的构建和绘制的。下面我按照我的理解来一一介绍这些类的作用和被创建时机,有不对的地方还请大家指正:

  • Activity:一个继承ContextThemeWrapper的类,继承自ContextThemeWrapper那么意味着,通过Activity可以访问当前包的资源(getResources、getAssets)和启动其他组件(Activity、Service、Broadcast)以及得到各种服务(getSystemService),并且定义了当前Activity的Theme主题类型 ,关于Context的解释请看:http://www.cnblogs.com/android100/p/Android-Context.html
    并且只有创建了一个Activity才会创建后面的PhoneWindow、DecorView、ViewRootImpl、WindowManagerImpl、和Surface这些类。我这里并没说ActivityThread类,ActivityThread是Android应用的主线程(UI线程),一个应用进程才有一个UI线程。也没有说WindowManagerGlobal类,这个类用了几个数组管理一个应用进程内所有Activity的DecorView和ViewRootImpl以及WindowManager对应关系的。WindowManagerGlobal是一个单例的类,一个应用进程内也只有一个。但是WindowManagerImpl是每一个Activity都有一个的。Activity是在ActivityThread的performLaunchActivity方法中用ClassLoader类加载器创建出来的。

  • PhoneWindow: 一个继承于抽象类Window的类,也是Window的唯一实现类。在Activity中PhoneWindow处在顶级位置,但是PhoneWindow是一个不可见的类,PhoneWindow内的DecorView才是我们可见的UI布局,但是我们可见的UI布局为啥要包装一层PhoneWindow列?经过我的多次查看源代码和推敲,我发现PhoneWindow负责了Activity UI界面顶级布局DecorView的创建,PhoneWindow保存了window attributes即窗口布局属性参数,保存了与WindowManagerService进程通讯的IBinder (取名token)方便去系统WindowManagerService进程通讯,并且负责了用户Key和Touch事件的分发,但是PhoneWindow分发事件也是交给了DecorView来完成的。除了我们Activity在setContent时PhoneWindow创建了DecorView,好像PhoneWindow并没有负责太多的Render的事件。看源码确实就是如此啊!,其实真正的Render操作都在ViewRootImpl类中。PhoneWindow是在Activity的attach方法中new出来的。

  • WindowManagerImpl: 见名知意,就是管理Window的,在这里就是管理PhoneWindow的,每一个Activity都有一个自己的WindowManagerImpl来管理自己的PhoneWindow,WindowManagerImpl其实是用来管理PhoneWindow里面的DecorView的,也可能是DecorView级别的其他的View,还有就是WindowManagerImpl是用来跟一个App全局的PhoneWindow管理器WindowManagerGlobal通信的,添加、移除和更新DecorView层级的View时WindowManagerImpl会调用WindowManagerGlobal中的方法来进行全局管理。WindowManagerImpl是在Activity的attach方法中new PhoneWindow后调用mWindow.setWindowManager方法创建的,其内部是调用setWindowManager的createLocalWindowManager方法new出来的。

  • DecorView: 是一个ViewGroup,继承自FrameLayout,是我们看到的UI界面的顶级容器,DecorView是PhoneWindow的成员。DecorView除了是Activity界面的顶级容器以外,DecorView还是key和touch事件从上向下真正分发开始的源头。事件分发从Activity—》PhoneWindow—》DecorView—》层层向下分发到我们的界面底层。DecorView其实在很多地方都会创建,PhoneWindow内部的getDecorView方法被调用时,要是内部的成员变量mDecor为空的话,就会调用installDecor()方法去创建一个DecorView,最后new 一个DecorView返回,保证调用PhoneWindow的getDecorView获取 DecorView时永远不会为空。我看代码发现PhoneWindow的DecorView第一次调用发生在ActivityThread内的handleResumeActivity方法中,handleResumeActivity方法中还有一个很关键的步骤,就是调用了WindowManagerImpl的addView(DecorView,WindowManager.LayoutParams )方法,进去创建了一个跟DecorView关联的ViewRootImpl类。

  • ViewRootImpl:是一个实现了ViewParent接口的final类。我前面说到了,ViewRootImpl主要是负责Activity的界面Render绘制的,负责整个窗口界面的ViewTree的绘制更新。ViewRootImpl要负责DecorView的绘制,那么ViewRootImpl就需要持有当前Activity的DecorView的引用,是的,的确如此,前面介绍DecorView的创建的时候我说到了DecorView第一次创建发生在ActivityThread内的handleResumeActivity方法中,handleResumeActivity方法中还有一个很关键的步骤,就是调用了WindowManagerImpl的addView(DecorView,WindowManager.LayoutParams )方法,进去创建了一个跟DecorView关联的ViewRootImpl类。WindowManagerImpl的addView方法其实调用了WindowManagerGlobal的addView方法,在这个方法中创建了ViewRootImpl,并且在这个方法中调用了ViewRootImpl的setView方法,使ViewRootImpl持有DecorView的引用。并且把实现了ViewParent接口的自己设置成了DecorView的Parent。这样ViewRootImpl就可以肆无忌惮地操作DecorView了。其实我在调用普通view的invalidate()刷新时,其实是经过层层Parent寻找,最终调用了最顶层的ParentViewRootImpl的invalidate()来判断和处理每次的UI界面刷新的。

  • Surface:一个实现了Parcelable接口的类,对Parcelable接口不了解的,请看我之前的Binder通信的文章:Android IPC之AIDL看这一篇还不够Surface是原始图像缓冲区(raw buffer)的一个句柄,而原始图像缓冲区是由屏幕图像合成器(screen compositor)管理的。得到了Surface这个句柄就可以得到其中的Canvas、原生缓冲器以及其它方面的内容。所以说Surface是生成Canvas的地方,具体就是调用lockCanvas方法获得Canvas。至于Canvas、Paint 和Bitmap的关系我这里就不讲了,后面会有文章来讲这个。Surface是ViewRootImpl的一个final成员变量,伴随ViewRootImpl的创建默认就new一个出来了,但是此时的Surface是一个空的,里面是没有内容的。Surface的数据填充是要跟WindowManagerService交互的,应用进程端Binder通知WindowManagerService进程端创建一个Surface对象,最后是将WindowManagerService进程端的 Surface对象传递到应用进程端并赋值给应用进程的Surface对象,这样窗口可以使用Surface来绘制UI了。因为Surface对象要夸进程传递,所以Surface要实现Parcelable接口。更详细的介绍关于窗口的Surface创建请看老罗的:Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析

二、Activity窗口创建流程

上面的名词介绍其实就是按照窗口的创建流程的顺序来讲的,但是单单是文字不够直观,下面以箭头加方法名的形式展现一下,源码分析流程基于Android7.1



/**1*/ ApplicationThread的onTransact方法接收到SystemServer进程的SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION启动Activity的Binder信息
                ↓
/**2*/ ApplicationThread.scheduleLaunchActivity() //
                ↓
/**3*/ ActivityThread.scheduleLaunchActivity() //安排启动Activity
                ↓
/**4*/ ActivityThread.handleLaunchActivity()  //处理启动Activity
                ↓
/**5*/ ActivityThread.handleResumeActivity() // Activity 的Resume会使DecorView跟ViewRootImpl关联
                ↓
/**6*/ WindowManagerGlobal.addView() //全局保存窗口的信息
                ↓
/**7*/ ViewRootImpl.setView()  //使DecorView和ViewRootImpl关联并绘制界面
                ↓
/**8*/ ViewRootImpl.requestLayout() //请求绘制ViewTree
                ↓
/**9*/ ViewRootImpl.scheduleTraversals() // 安排遍历 
                ↓
/**10*/ ViewRootImpl.doTraversal() //
                ↓
/**11*/ ViewRootImpl.performTraversals() //执行遍历 会根据情况调用relayoutWindow performMeasure performLayout performDraw 等方法 这四个方法跟绘制是紧密相关的
                ↓
/**12*/ ViewRootImpl.relayoutWindow() //窗口第一次创建或者是窗口大小有变化并且窗口可见就会调用此方法 
                ↓
/**13*/ ViewRootImpl.mWindowSession.relayout() //binder通信通知WindowManagerService创建一个跟应用端关联的Surface
                ↓
/**14*/ ViewRootImpl调用performMeasure performLayout performDraw方法绘制UI


其实第五步ActivityThread.handleResumeActivity()方法内会调用到WindowManagerGlobal.addView()方法把窗口绘制完成,紧接着就调用到了activity.makeVisible()方法,其实就是显示出DecorView,因为DecorView创建绘制前是被设置成了INVISIBLE的,Activity中的makeVisible方法就是把绘制完成的DecorView显示出来了:

    void makeVisible() 
        if (!mWindowAdded) 
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        
        mDecor.setVisibility(View.VISIBLE); //把DecorView设置成可见的
    

至此我们就看到Activity界面上的UI了。

关于Activity的绘图表面(Surface)的创建过程我还想多说一下,以及Surface跟Activity的对应关系。

Surface是ViewRootImpl的一个final成员变量,伴随ViewRootImpl的创建默认就new一个出来了,但是此时的Surface是一个空的,里面是没有内容的。Surface的数据填充是要跟WindowManagerService交互的,应用进程端Binder通知WindowManagerService进程端创建一个Surface对象,最后是将WindowManagerService进程端的 Surface对象传递到应用进程端并赋值给应用进程的Surface对象,这样窗口可以使用Surface来绘制UI了。因为Surface对象要夸进程传递,所以Surface要实现Parcelable接口。

从Activity窗口创建的流程我们可以知道:

  • 每一个应用程序窗口都对应有两个Java层的Surface对象,其中一个是在WindowManagerService服务这一侧创建的,而另外一个是在应用程序进程这一侧创建的。

  • 在WindowManagerService服务这一侧创建的Java层的Surface对象在C++层关联有一个SurfaceControl对象,用来设置应用窗口的属性,例如,大小和位置等。

  • 在应用程序进程这一侧创建的ava层的Surface对象在C++层关联有一个Surface对象,用来绘制应用程序窗品的UI。

三、Activity窗口组件之间的对应关系

一个设备有一个WindowManagerService进程 有一个SystemServer进程

一个App有一个WindowManagerGlobal类 有一个ActivityThread类 有一个ApplicationThread类

一个App可以有很多个Activity

一个 Activity有一个PhoneWindow类

一个PhoneWindow类有一个ViewRootImpl类

一个PhoneWindow类有一个WindowManagerImpl类

一个ViewRootImpl类有一个Surface类 有一个DecorView类

用一张图表示:

 

Android设备构成图

参考文章:
Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析
从源码看invalidate和requestLayout的区别
Android Render系列规划篇
Android应用层View绘制流程与源码分析


 

以上是关于Android RenderActivity窗口构成和绘制解析的主要内容,如果未能解决你的问题,请参考以下文章

2-Qt关闭子窗口时执行特定代码

Qt窗口关闭和应用程序停止是否调用析构函数的一些说明

Qt窗口关闭和应用程序停止是否调用析构函数的一些说明

Android Gradle 构建有大小限制吗?

android和ios的cordova构建都失败了

qt 里面怎么把窗口释放掉