View-Window-Surface
前言
本文打算从Root View的建立出发,呈现View,Window,Surface之间的联系。
从而厘清这三者之间的不同,加深对Android Graphic系统的理解
V-W-S简介
View:android中最基本的UI组件,会占据屏幕上的一席之地并可以处理一些基本事件。This class represents the basic building block for user interface components. A View
occupies a rectangular area on the screen and is responsible for drawing and
event handling. View is the base class for widgets, which are
used to create interactive UI components (buttons, text fields, etc.).Window:Window是top View的容器,这里的top view我们一般都是说ViewRoot。Window的概念在Android中比较抽象,广义上,被认为是一个App对应了一个window,狭义上,Window实际又对应了
ViewRoot,对于WindowManagerService来说,如何呈现/管理App端的显示,都是通过Window来实现的Surface:Surface可以认为是一块提供绘制的buffer,所有View的修改,最终都会被统一反馈到Surface上。每个ViewRoot都会有一个真实的surface变量,View的所有操作会反馈到这块Surface。这块Surface也即SurfaceFlinger
的某一个composer对象,因此只有操作到了ViewRoot中的Surface,屏幕上才会有变化
注意:此处的Surface并不是SurfaceView,SurfaceView本质上还是一个View,注意区分
RTFSC
为了更深刻的理解这个问题,本文打算从ViewRoot的建立一路讲述,一直到三者的联系完全建立未知。
setContentView
对于
setContentView相信大家都不会陌生,一般都是传入一个Resouce ID,这样整个Activity的视图就建立了。
ok,对于
Activity来说,其实是透过getWindow,来做setContentView,那么我们看看对应的Window是谁。
mWindow是Activity类内部的一个私有变量,顺藤摸瓜,我们看一下mWindow是从哪里被new出来的。如果你熟悉Activity的创建过程,应该就知道了,mWindow其实是在
performLaunchActivity的时候,调用Activity.attach的时候创建的。
所以,下面我们需要去看看
PhoneWindow.setContentView顺便插一句,
PhoneWindow这个类比较重要,而它本身是extends Window
可以看到对于传入的
layoutResID,最终会通过inflate创建出对应的View
但是整个过程中并没有看到ViewRoot,所以我们还需要看看划红线的installDecor通过上图可以看到,当且仅当
mContentParent == null的时候才会走进去,那么mContentParent是哪里赋值的呢?
这里又平白无故多出了个
mDecor,什么是private DecorView mDecor
我们先通过视图查看神器hierarchyviewer搂一眼,先看看第一个initXXX

显然,DecorView是一个Root View,如果这个不清楚..那么请看下图

让我们来聚焦到:
mDecor = generateDecor(-1);
直接了当,继续往下看看
new DecorView首先,
DecorView是extends自Framelayout,从本质上,DecorView是一个容器Viewgroup,也是一个View

再来看看真正的
new DecorView
通过
setWindow函数,会建立起PhoneWindow与DecorView建立联系,因此,DecorView中就会持有PhoneWindow的引用- 至此,整个
mDecor = generateDecor(-1);的流程就走完了,简单来说DecorView是一个ViewGroup,hierarchyviewer中也可以看出来,整个DecorView是视图的Root- 建立了
PhoneWindow与DecorView
- 至此,整个
我们走完了
generateDecor,继续往下走,来到mContentParent = generateLayout(mDecor);
整个
generateLayout分为4步- 根据一系列判定条件选择合适的
layoutResource - 通过对应的
layoutResource进行inflate - 透过整个
ViewTree获取contentParent - 这个
contentParent,就是上面提到的mContentParent
其中比较重要的是步骤二和步骤三,也即:
- 透过整个
ViewTree获取contentParentmDecor.onResourcesLoaded(mLayoutInflater, layoutResource); - 这个
contentParent,就是上面提到的mContentParentViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
我们直接看一下
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);:
- 其中
createDocrCaptionView的部分可以略过,根据原生的代码,这一块主要是给FreeForm来使用的
如下图所示:
inflate也没有什么可说的,第二参数为null,表示无默认Parent ViewaddView亦是常规操作,显然是把刚才inflate出来的root作为child viewadd到DecorView- 最后一波
mContentRoot = (ViewGroup) root;整个操作结束
我们回头再看一下
DecorView三剑客
mWindow:在DecorView初始化的时候就已经建立了联系mContentRoot:通过feature选择的最恰当layoutResource,进而通过inflate创建的ViewmDecorCaptionView:FreeForm的“action bar”
再来看看
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);:



以上4支api完成了整个findViewById的操作,通过之前的认知,整个mDocr是Window/ViewTree的root
而对应的ID_ANDROID_CONTENT,则是整个ViewTree的Root下的第一个View,而这个ID_ANDROID_CONTENT
这个id,确实也是在frameworks res里面会用到,也就是那些默认的layout们
到这里,我们把mContentParent = generateLayout(mDecor);走完了,简单总结一下:- 通过
generateLayout,基于mDecor又添加了一个子View,ID:ID_ANDROID_CONTENT(视情况不同可能为CaptionView) - 最终
mContentParent永远都是ID:ID_ANDROID_CONTENT的View
- 根据一系列判定条件选择合适的
走完了上面一长串流程,其实是在建立
mDecor/PhoneWindow/mContentParent之间的联系,而我们通过setContentView传入的那个Resource ID,压根都还没有用到。
重新看这一段,整个installDecor就完成了。
下面才是用到了
layoutResID,通过inflate,把这个layoutResID对应的View放到mContentParent那么这个
mContentParent是谁呢?再回头看,其实就是generateLayout中的return contentParent,也就是对应的ID:ID_ANDROID_CONTENT
因此,整个setContentView的所有逻辑就走完了。
- 通过
installDecor,把PhoneWindow/DecorView/mContentRoot建立起联系 - app端真正传入的
ResouceID,会通过inflate出一个View,把它加到mContentRoot也即ID:ID_ANDROID_CONTENT
小结:
对于app端来说setContentView是整个ViewTree建立的入口,也是好多Demo中onCreate必撸的代码。
当了解了setContentView传入的resID最终会变成哪个View,变成谁的Child View,厘清这一点对于debug很有必要。
UML两张流
UML类图
比较重要的是三个类:Activity,PhoneWindow,DecorView
UML时序图
对应的流程图:
View与Window-MnagerService
先前的setContentView帮助我们在app内部建立了一套完整的ViewTree
但是自始至终,我们都没有发现App与System_Server之间发生任何的关系,通过我们认识,Window只有被add到了WindowManagerService之后才会有机会被显示出来。
一点点基础知识
- 顺着这个思路,我们继续往下走,这边直接来到
ViewRootImpl的构造函数处:
以上Call Stack是既成事实的,但是我们仍旧要看一下传入的各个参数:

这里的r,其实是
Activity
对应的r.window,就是PhoneWindow
而r.window.getDecorView(),其实就是对应的DecorView
因此整个ViewManager最后会把decor添加进去根据一系列的继承关系:
WindowManagerImpl-|>WindowManager-|>ViewManager,所以就来到了WindowManagerImpl
这里的
mGlobal又是一个典型的桥接模式
View / Window / WMS
在
mGlobal的addView中,就会去创建ViewRootImpl,从而建立起与WMS的整个联系,也就是串起了View和Window。
ViewRootImpl的初始化,是一个非常重要的过程:
对于第一步,我们先来看看
mWindowSession = WindowManagerGlobal.getWindowSession();其中
mWindowSession是一个IPC的Proxy对象:IWindowSession

对应的native实体,其实是
Session,而这个Session则是WindowState中的一个的对象:


WindowState的话,则是WindowManagerManager中重要的对象(基本上可以认为一个client端的window,就是WMS端的一个WindowState再来看看第二步:
mWindow = new W(this);,对于这个W,它继承自IWindow.Stub
这里引入出了一个
IWindow的概念,由于在App端我们持有的是Stub,因此,在WMS那边的话其实就是对应的Proxy了整个
ViewRootImpl的初始化,我们有两个重要对象mWindowSession以及mWindow,我们先mWindowSession:是继承自IWindowSession,也即App端,是一个ClientmWindow:是继承自IWindow.Stub,也即App端,是一个Server
完成了
ViewRootImpl的初始化,我们继续往下走:root.setView(view, wparams, panelParentView);这里的
root,其实就是ViewRootImpl,因此这边其实是调用了ViewRootImpl.setView
- 整个
setView的过程,其实是在建立App端与Server的联系,也即App与WMS
这里的
requestLayout其实也有其特定作用,但本文先不分析了,我们直接看mWindowSession.addToDisplay- 整个
根据我们对
AIDL的认识,可以知道这边会直接跑到Server端运行,而之前也知道,对应的Server端其实是Session对象,因此这边的mWindowSession.addToDisplay,就会走到Session.addToDisplay
- 其中,传入的第一个参数
window,在App端其实就是IWindow.Stub实例mWindow Session中的mService,实际是WindowManagerService
- 其中,传入的第一个参数
因此,
Session.addToDisplay最终就跑到了WindowManagerService.addToDisplay
整个addWindow的流程,针对当前的流程来梳理,我们需要看上述的第一步和第二步。其中第一步,
session对应了App端的mWindowSession,client对应的则是App端的mWindow
这里的
mWinAnimator,在WMS里面往往是用来做各类动画的基础第二步,
win.attach:
这里的
mSession是之前在构造函数中赋值的Session实例。
通过这个windowAddedLocked,Session被mService.mSessions.add(this)与WMS建立了联系
同样:mSurfaceSession = new SurfaceSession();这个地方,Session才真正与SurfaceFlinger建立了联系

小结
ViewRootImpl的初始化过程中,会带出mWindowSession以及mWindow。
其中,mWindowSession是ISession在App端的Proxy,而mWindow则是IWindow在App端的ISession。
因此,两者的地位和作用也是不同的,mWindowSession是用来与WMS做通讯的,而mWindow则是WMS用来回调App的。
UML两张流
UML类图

点评一下这张令人头大的UML蜘蛛图:
Activity中会持有mWindow,它是PhoneWindow的实例PhoneWindow继承自WindowPhoneWindow中,会持有DecorView;同样DecorView也会持有PhoneWindow的实例DecorView集成自FrameLayout,这一路是从ViewGroup,ViewParent,ViewManager下来的。
上述内容是之前setContentView中的东西
ViewRootImpl被WindowManagerGlobal所持有WindowManagerGlobal通过桥接模式,被WindowManagerImpl所持有,而WindowManagerImpl实现了WindowManager的接口ViewRootImpl会持有IWindowSession的实例mWindowSessionIWindowSession继承自IInterface,它是一个Client端,运行在App进程,System_Server是Server端ViewRootImpl会持有ViewRoot.W的实例mWindowViewRootImpl.W是继承自IWindow.Stub,它是一个Server端,运行在App进程,System_Server是Client端
UML时序图
