Android Render(一)Activity窗口构成和绘制解析


阅读者三篇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绘制流程与源码分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容