[转载]图解Android - Android GUI 系统 (2) - 窗口管理系统 - 漫天尘沙 - 博客园

[转载]图解Android – Android GUI 系统 (2) – 窗口管理系统 – 漫天尘沙 – 博客园.

图解Android – Zygote 和 System Server 启动分析一 文里,我们已经知道Android 应用程序是怎么创建出来,大概的流程是 ActivityManagerService -> Zygote -> Fork App, 然后应用程序在ActivityThread 中Loop 进入无限的等待循环中处理来自AcitivyManagerService的消息。如果一个Android的应用有Acitivity, 那它起来后的第一件事情就是将自己显示出来,这个过程是怎样的? 这就是本章节要讨论的话题。

Android 中跟窗口管理相关(不包括显示和按键处理)主要有两个进程,Acitivty所在进程 和 WndowManagerService 所在进程(SystemServer).  上图中用不同颜色区分。它们的分工是,Activity进程内主要完成的是窗口内View的管理,而WindowManager Service 管的是来自与不同的Acitivity的窗口。

1. Acitivty显示前的准备工作

图解Android – Zygote, System Server 启动分析中 我们已经知道,一个新的应用被fork完后,第一个调用的方法就是 ActivityThread的main(),这个函数主要做的事情就是创建了一个ActivityThread线程,然后loop()开始等待。当收到 来自 ActivityManager 的 LAUNCH_ACTIVITY 消息后,Activity开始了他的显示之旅。下图显示了Activity在显示前的准备流程。


图中分为三大块, 最右上角是Acitivity应用的初始化。中间部分是Acitivity Window的显示准备工作,最左边这张图,则是window真正的显示过程。因为本章关注的是Window显示前的准备工作,我们把注意力放在中间那部分。

addView() 中ViewRootImpl 被创建出来,在概论 一文中我们提过ViewRootImpl 的地位相当与MVC架构中的C,Controller的创建意味着这里才是真正的开始。ViewRootImpl 构造函数里创建了一个Surface对象(注意这是一个空的Surface,里面没有有用的信息),然后就是通过OpenSession() 创建了一个和WindowManagerService 的通话通道,然后想WMService 报道,并将自己加入到WindowManager的窗口队列中。图中土黄色部分表示在WindowManagerService 进程里运行,它在这里做了几件重要的事情,一件是new了一个WindowState对象,他是ViewRootImpl 在WMService端的代理。然后openInputChannelPair() and RegisterInputChannel(), 将这个新建窗口告知InputManagerService, 告知它接下来的用户输入事件对应的响应窗口(这部分会在Android的用户输入处理 一文中介绍),最后他创建了一个Surface Session. 看第一张类图,SurfaceSession 在native 端 有对应的实现,他最终又引用到ISurfaceComposerClient. 看到IXXX 意味着走到了一个进程的边界。对了,远处就是Surface Flinger。ISurfaceComposerClient 提供了两个接口,createSurface 和 createDisplay, 难道Surface 就在这里创建?  非也,现在还不是时候,Surface会在真正要显示的时候创建。到这里,Activity和WindowManager Service 已经建立起联系,WindowManager Service 里的工作也暂告一个段落,工作又回到了Activity(绿色)一端,makeVisible() -> invalidate() -> scheduleTravesal() -> postCallback … ViewRootImpl 向Choreographer对象注册了一个回调函数,然后对外发送ACTIVITY_RESUMED, 结束这一阶段准备工作?

Surface还没有创建,显示还没有开始,怎么就ACTIVITY_RESUMED了? 因为真正的显示在最多16ms以后就会开始。

2. Choreographer 和 Surface的创建

所有的图像显示输出都是由时钟驱动的,这个驱动信号成为VSYNC。这个名词来源于模拟电视时代,在那个年代,因为带宽的限制,每一帧图像都有分成 两次传输,先扫描偶数行(也称偶场)传输,再回到头部扫描奇数行(奇场),扫描之前,发送一个VSYNC同步信号,用于标识这个这是一场的开始。场频,也 就是VSYNC 频率决定了帧率(场频/2). 在现在的数字传输中,已经没有了场的概念,但VSYNC这一概念得于保持下来,代表了图像的刷新频率,以为着收到VSYNC信号后,我们必须将新的一帧进 行显示。

VSYNC一般由硬件产生,也可以由软件产生(如果够准确的话),Android 中VSYNC来着于HWComposer,接收者没错,就是Choreographer。Choreographer英文意思是编舞者,跳舞很讲究节奏不 是吗,必须要踩准点。Choreographer 就是用来帮助Android的动画,输入,还是显示刷新按照固定节奏来完成工作的。看看Chroreographer 和周边的类结构。

 

从图中我们可以看到, Choreographer 是ViewRootImpl 创建的,它拥有一个Receiver, 用来接收外部传入的Event,它还有一个Callback Queue, 里面存放着若干个CallbackRecord, 还有一个FrameHandler,用来handleMessage, 最后,它还跟Looper有引用关系。再看看下面这张时序图,一切就清楚了,

 

 

首先Looper调用loop() 后,线程进入进入睡眠,直到收到一个消息。Looper也支持addFd()方法,这样如果某个fd上发生了IO操作(read/write), 它也会从睡眠中起来。Choreographer实现用到了两种方式,首先他通过某种方式获取到SurfaceFlinger 进程提供的fd,然后将其交给Looper进行监听,只要SurfaceFlinger往这个fd写入VSync事件,looper便会唤醒。 Lopper唤醒后,会执行onVsync()时间,这里面没有做太多事情,而是调用Handler接口 sendMessageAtTime() 往消息队列里又送了一个消息。这个消息最终调用到了Handler (实际是FrameHandler)的handleCallback来完成上层安排的工作。为什么要绕这么大个圈?为什么不在onVSync里直接 handleCallback()? 毕竟onVSync 和 handleCallback() 都在一个线程里。这是因为MessageQueue 不光接收来自SurfaceFlinger 的VSync 事件,还有来自上层的控制消息。VSync的处理是相当频繁的,如果不将VSync信号送人MessageQueue进行排队,MessageQueue 里的事件就有可能得不到及时处理,严重的话会导致溢出。当然了,如果因为VSync信号排队而导致处理延迟,这就是设计的问题了,这也是为什么 Android文档里反复强调在Activity的onXXX()里不要做太耗时的工作,因为这些回调函数和Choreographer运行在同一个线程 里,这个线程就是所谓的UI线程。

言归正传,继续往前,VSync事件最终在doFrame()里调了三次doCallbacks()来完成不同的功能, 分别处理用户输入时间,动画刷新(动画就是定时更新的图片), 最后执行performTraversals(),这个函数里面主要是检查当前窗口当前状态,比如说是否依然可见,尺寸,方向,布局是否发生改变(可能是 由前面的用户输入触发的),分别调用performMeasure(), performLayout, performDraw()完成测量,布局和绘制工作。我们会在后面详细学习这三个函数,这里我们主要看一下第一次进入 performTraversals的情况,因为第一次会做些初始化的工作,最重要的一件就是如本章标题,创建Surface对象。

回看图2,我们可以看到Surface的创建不是在Activity进程里,而是在WindowManagerService完成的(颜色)。当一 个Activity第一次显示的时候,Android显示切换动画,因此Surface是在动画的准备过程中创建的,具体就是类 WindowStateAnimator.createSurfaced()函数。它最终创建了一个SurfaceControl 对象。SurfaceControl是Android 4.3 里新引进的类,Google从之前的Surface类里拆出部分接口,变成SurfaceControl,为什么要这样? 为了让结构更清晰,WindowManagerService 只能对Surface进行控制,但并不更新Surface里的内容,分拆之后,WindowManagerService 只能访问SurfaceControl,它主要控制Surface的创建,销毁,Z-order,透明度,显示或隐藏,等等。而真正的更新者,View会 通过Canvas的接口将内容画到Surface上。那View怎么拿到WMService创建的Surface,答案是下面的代码 里,surfaceControl 被转换成一个Surface对象,然后传回给ViewRoot, 前面创建的空的Surface现在有了实质内容。Surface通过这种方式被创建出来,Surface对应的Buffer 也相应的在SurfaceFlinger内部通过HAL层模块(GRAlloc)分配并维护在SurfaceFlinger 内部,Canvas() 通过dequeueBuffer()接口拿到Surface的一个Buffer,绘制完成后通过queueBuffer()还给 SurfaceFlinger进行绘制。

复制代码
                    SurfaceControl surfaceControl = winAnimator.createSurfaceLocked();
                    if (surfaceControl != null) {
                        outSurface.copyFrom(surfaceControl);
                        if (SHOW_TRANSACTIONS) Slog.i(TAG,
                                "  OUT SURFACE " + outSurface + ": copied");
                    } else {
                        outSurface.release();
                    }
复制代码

 

到这里,我们知道了Activity的三大工作,用户输入响应,动画,和绘制都是由一个定时器驱动的,Surface在Activity第一次启动 时由WindowManager Service创建。接下来我们具体看一下View是如何画在Surface Buffer上的,而Surface Buffer的显示则交由图解Android – Android GUI 系统 (3) – Surface Flinger 来讨论。

 

3. View的Measure, Layout 和 Draw

直接从前面提到的performMeasure()函数开始.

实际的函数调用栈比这里深的多得多,这个函数会从view的结构数顶(DecorView), 一直遍历到叶节点。但大致可以归纳成图中的三种类,DecorView, ViewGroup 和 View, 它们的类结构如下图所示:

所以可见的View(不包括DecorView 和 ViewGroup)都是一个矩形,Measure的目的就是算出这个矩形的尺寸, mMeasuredWidth 和 mMeasuredHeight (注意,这不是最终在屏幕上显示的尺寸),这两个尺寸的计算受其父View的尺寸和类型限制,这些信息存放在 MeasureSpec里。MeasureSpec 里定义了三种constraints,

  • 复制代码
            /* 父View对子View尺寸没有任何要求,其可以设任意尺寸*/
            public static final int UNSPECIFIED = 0 << MODE_SHIFT;
    
            /* 父View为子View已经指定了大小*/
            public static final int EXACTLY     = 1 << MODE_SHIFT;
    
            /*父View没有指定子View大小,但其不能超过父View的边界 */
            public static final int AT_MOST     = 2 << MODE_SHIFT;
    复制代码

widthMeasureSpec 和 heightMeasureSpec 作为 onMeasure的参数出入,子View根据这两个值计算出自己的尺寸,最终调用 setMeasuredDimension() 更新mMeasuredWidth 和 mMeasuredHeight.

performMeasure() 结束后,所有的View都更新了自己的尺寸,接下来进入performLayout().

 

4. Windows 的管理

我们再次回到WindowManager Service 内部研究它的工作原理。

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏