(源码截图是Android 5.1.1_r6)
一 App和Surface的关系是怎样的
![](https://img-blog.csdnimg.cn/img_convert/62b7b8fb84795fd1f2ff95f09567a1fc.png)
不论是用Skia绘制二维图像,还是用OpenGL绘制三维图像,最终Application都要和Surface交互。Surface 是什么:
Handle onto a raw buffer that is being managed by the screen compositor.
由屏幕显示内容合成器(screen compositor)所管理的原始缓冲区的句柄,就像在C++语言中,可以通过一个文件的句柄,就可以获得文件的内容一样,通过Surface就可以获得原生缓冲器以及其中的内容,那App是怎么通过Surface获取的缓冲区去绘图的呢?
应用程序的外表是通过Activity显示的,首先我们来看一下Activity是怎么完成界面绘制工作的。从Activity启动开始跟。
Zygote在收到启动请求的时候,会fork一个子进程,这个子进程就是app的对应的进程,它的入口函数就是ActivityThread类的main函数,这个类里面有一个handleLaunchActivity函数,它就是创建Activity的地方。
![](https://img-blog.csdnimg.cn/img_convert/ff6bdc004501739f5b08ce07038e7717.png)
然后接着看Activity创建的代码performLaunchActivity
![](https://img-blog.csdnimg.cn/img_convert/37410c1facd5064187888fe6d02f0604.png)
Instrumentation.java
![](https://img-blog.csdnimg.cn/img_convert/98b1e4c6ea75087b69eb9cdfe88100d4.png)
跳转到Activity attach中
![](https://img-blog.csdnimg.cn/img_convert/a640e5e33a45893c9bf27c7fc82503b7.png)
Window是个抽象类,那当前Activity中的这个mWindow的具体实现类是哪个?
继续跟这个PolicyManager
![](https://img-blog.csdnimg.cn/img_convert/1a764e2c89ceea24a82ef0329072fd86.png)
这里获取了return 了 com.android.internal.policy.impl.Policy 的这个类 makeNewWindow(context)方法,继续跟Policy这个类
![](https://img-blog.csdnimg.cn/img_convert/de84cda98181cfedd50506f724ab5ad6.png)
这里发现了,Activity中存在的Window是PhoneWindow。
然后继续Activity创建好后的方法handleResumeActivity();
![](https://img-blog.csdnimg.cn/img_convert/e0722b81f3ee7b8fe643de9a94f5d0cc.png)
这里三个方法都要跟,先看第一个r.getWindow.getDecorView();
r是ActivityClinetRecord 是Activity描述的类,对Activity进行了高度抽象,记录了Activity的各种信息,大家应该都清楚,这里就不再赘述了。
1.window.getDecorView。
ActivityClinetRecord记录着是是Activity的信息,所以这个window就是刚刚说的Activity里面的PhoneWindow,我们跟一下PhoneWindow这个方法
![](https://img-blog.csdnimg.cn/img_convert/5298bec7b1d8d1a8b1e429ab275a6e42.png)
再看一下installDecor()方法,这个方法是不是有点眼熟,setContentView就是调的这个,我们看一下SetContentView
![](https://img-blog.csdnimg.cn/img_convert/49dab6126fbd82c167535772dbc84d81.png)
再跟一下这个installDecor
![](https://img-blog.csdnimg.cn/img_convert/6b318c627ccf526b4bc4613cf2b04b65.png)
如果用户没有传这个layout,mContentParent就是decorView的子布局
![](https://img-blog.csdnimg.cn/img_convert/73bc4c3ddb8599ee6fd5a29350031d9a.png)
再看一下generateLayout(mDecor)这个方法,方法太多截取了一下
![](https://img-blog.csdnimg.cn/img_convert/5fd9e187aa3bd756fad96e38e64025f8.png)
再看一下这个findViewById
![](https://img-blog.csdnimg.cn/img_convert/7e0a2e91832a63d54b271b8a74a44ed5.png)
通过这个其实就能看出,Activity的onCreate函数中,通过setContentView设置的View其实只是DecorView的子View,DecorView还处理了标题栏显示等一系列的工作
2. 第二个方法 :ViewManager wm =a.getWindowManager()
这个WM的具体实现类是什么?
a是Activity,Activity的getWindowManager方法看一下
![](https://img-blog.csdnimg.cn/img_convert/35b2730431dcd7fad5f906d08cb8d486.png)
这个mWindowManager上面跟过了,是从mWindow里面拿出来的,mWindow的类型是PhoneWindow之前也跟过了,想要知道这个wm是什么类型的,继续跟PhoneWindow的setWindowManager方法
发现里面没有这个方法,PhoneWindow继承Window看看父类
![](https://img-blog.csdnimg.cn/img_convert/6efe9974414a5326a1a9972cc422293c.png)
这里有个小细节,4.2以下和4.2及以上的实现不太一样,下面是4.1的Window的源码
![](https://img-blog.csdnimg.cn/img_convert/0a26aca4bffa40b42c4f8d59e0b6b882.png)
看一下这个LocalWindowManager是什么
![](https://img-blog.csdnimg.cn/img_convert/19f0e2c0c1b55b695bb0544b74fd226e.png)
LoaclWindowManager是Window的一个内部类,LocalWindowManager可以算个代理类,里面很多方法的具体实现还是在WindowManagerImpl。从 Android 4.2开始就已经没有这个东西了
下面是4.2及以上的代码,返回的只是WindowManagerImpl这个类的实例
![](https://img-blog.csdnimg.cn/img_convert/596e3255d1fb51bee5b7fef50a9a7e57.png)
![](https://img-blog.csdnimg.cn/img_convert/5dce1a8411deb4ad8820366f89cebcfd.png)
这里就已经知道了Activity内缓存的WindowManager类型(4.2之前是LocalWindowManager,4.2及4.2之后是WindowManagerImpl)
这里可以顺便把之前Window 和View的关系理一下
![](https://img-blog.csdnimg.cn/img_convert/9100434407c4b6245d48b9d58c880674.png)
3.这里知道wm的具体实现类型了,就可以跟最后一个方法了,wm.addView()。
其实4.2以下的LocalWindowManager也是调用的super.addView(),所以这里统一看WindowManagerImpl 中的addView(decor,l),这里也有个小细节,4.2及4.2版本以上引入了一个新的WindowManagerGlobal概念,这里以5.1的源码为准
![](https://img-blog.csdnimg.cn/img_convert/0823f0cf70b0ba9d70c395fdea0d33e7.png)
![](https://img-blog.csdnimg.cn/img_convert/a152ab9c03dda27007a2d7b88cbb8fd9.png)
提供与系统窗口管理器不需要上下文的低级别通信,看WindowManagerGlobal的addView方法
![](https://img-blog.csdnimg.cn/img_convert/41f6f68a43c0fec5501ba2018acd966b.png)
然后再看一下ViewRootImpl
![](https://img-blog.csdnimg.cn/img_convert/a4a9cc862751314c55abf96cf8110085.png)
好了,终于是发现Surface的踪迹了。看一下这个Surface的无参构造方法
![](https://img-blog.csdnimg.cn/img_convert/dcb3f5d1d53b92bdda4f9496475940d3.png)
里面什么都没做,只是创建了一个空的surface对象,W mWindow 是一个基于Binder通信的类,从IWindow.sutb派生出来的,这里先不管。
好了,这里Surface出现了,总结一下之前干的事情
首先跟踪了Acitivity在启动的时候做的一些事情,在performLaunchActivity(),中发现了Activity中存储了一个Window,一个WindowManager,这个WindowManager也同时存在Window里面,Window的类型是PhoneWindow的
在handleResumeActivity()中跟踪了三个方法
1.r.window.getDecorView(),
1.1在r.window.getDecorView()发现了DecorView和Activity 具体setContentView的关系,
2.a.getWindowManager(),
2.1在a.getWindowManager(),理清楚了Window ,WindowManager,Activity,DecorView,MyView的关系,
注: ” -->“ 继承
Activity里面缓存了PhoneWindow -->Window(抽象类) 和
WindowManager的实例,WindowManager缓存在Window当中,
WindowManager实例在4.2以下是 LocalWindowManager(Window的一个
内部类),4.2及4.2以上是
WindowManagerImpl -->WindowManager(interface) --
>ViewManager(interface)
3.在wm.addView(),发现了ViewRootImpl里面缓存了一个我们需要了解的Surface实例
接下里具体分析缓存了Surface实例的ViewRootImpl
二 :ViewRootImpl
继续跟上面ViewRootImpl的setView方法
![](https://img-blog.csdnimg.cn/img_convert/611bdd564428b70ea7e17a6dcc76c86d.png)
![](https://img-blog.csdnimg.cn/img_convert/1fda12cd60d649f1a53dae80f3499925.png)
先看到这个mWindowSession,他的实际类型是IWindowSession,看到这个大家应该就都明白咋回事了,这个是Session代理对象,就是WMS里面的那个成员变量mSessions里面装着的东西,如下图WindowManagerService的成员变量
![](https://img-blog.csdnimg.cn/img_convert/ccfdd6c5164e2cad803f97c72d399ed3.png)
Session继承于IWindowSession.Stub, 作为Binder服务端,同一进程中所有的ViewRootImpl对象只对应唯一相同的Session代理对象,所以这个addToDisplay()的函数是一个跨进程的调用,具体实现在Session类里,这里是ViewRootImpl和WMS的跨进程通信,和主题没什么关系,先不管它。
先继续跟requestLayout();
![](https://img-blog.csdnimg.cn/img_convert/04dd4d22817eb16f2baf07f6d905daf9.png)
子线程不能刷新UI就是这个方法,ViewRootImpl里面有很多地方都调用了这个方法,这里就不一一贴了,由于本篇最之前看了,他是在onResume里面才被创建的,所以在某些极端情况onCreate里面在子线程刷新也是能成功的。
![](https://img-blog.csdnimg.cn/img_convert/3f1be7ea75babd9d01ae28554d15e121.png)
![](https://img-blog.csdnimg.cn/img_convert/945ec310de8e8c82eb5512c2bf1fcd7d.png)
![](https://img-blog.csdnimg.cn/img_convert/d7940af6f53ec4afb30914098714c1a6.png)
![](https://img-blog.csdnimg.cn/img_convert/cbc551bca0bbbe0690bdb26dff98a373.png)
这边贴一个网图,就不再贴代码了,这里面大家应该都非常熟悉,主要工作是以DecorView为父容器开始自上而下的measure、layout、draw,measure确定View的测量宽和高,layout确定View的最终宽/高和四个顶点位置,draw将View绘制到屏幕上。
![](https://img-blog.csdnimg.cn/img_convert/b72964eb8155d4ec96323492a74382e8.png)
这个方法里面有两个地方和Surface相关,一个是relayoutWindow(),一个是drawSoftware()。看一下这两个方法
1. relayoutWindow()方法
![](https://img-blog.csdnimg.cn/img_convert/3f12384e9442554542c37a04c25f03ca.png)
其他参数可以先不用管,只要知道最后的mSuface就是ViewRootImpl 成员变量里面new出来的空的surface对象。
mWindowSession上面讲了,实际处理在Session的relayout里面,看一下代码
![](https://img-blog.csdnimg.cn/img_convert/c6631cee731a78bfdcea8ada786c4959.png)
![](https://img-blog.csdnimg.cn/img_convert/711cf8d04a9f8f498386a728a38b34c0.png)
这个mService是WMS,再看一下WMS的 relayoutWindow方法
![](https://img-blog.csdnimg.cn/img_convert/f4799dbbb197f090363f7055774bfd07.png)
![](https://img-blog.csdnimg.cn/img_convert/277e4eda40c0831042484f9758b6947f.png)
先跟一下winAnimator的createSurfaceLocked,
![](https://img-blog.csdnimg.cn/img_convert/14297199fbf6dfcb6cd2c3dcfcbd92f3.png)
继续跟
![](https://img-blog.csdnimg.cn/img_convert/11d546443ed4918e7c219d7cbdc464d5.png)
mNativeObject我的理解是native端返回的内存地址,如果为0说明对应的对象没有创建成功或者异常了。
继续跟native代码
![](https://img-blog.csdnimg.cn/img_convert/bbcd04bb829befad622a9c7840129922.png)
这里主要做的事情是通过SurfaceControl的构造方法,把所有的信息和数据,都传到native层,在native层创建了一个SurfaceControl的对象,并将地址返回了。
SurfaceControl类可以看作是一个wrapper类:它对一些公开函数封装了一层,里面还有些Layer相关的东西暂且先不管它们。
再看一下Surface的copyFrom方法
![](https://img-blog.csdnimg.cn/img_convert/c3ae9af7acdd408ab77e331918cf2406.png)
继续跟native 层的创建
![](https://img-blog.csdnimg.cn/img_convert/bf2b39ab1097d88958d7613e0ae57dc3.png)
这边将之前native层创建的包含了所有信息和数据的SurfaceControl传到native层,通过这个control,在底层创建了一个含有真实数据信息的Surface,并将地址返回了,再看java层的 setNativeObjectLocked方法
![](https://img-blog.csdnimg.cn/img_convert/d28c05edf3398c1e511a9f18946c1080.png)
这个方法就是把本地的ViewRootImpl 中的mSurface的内存地址替换了,换成了native层的地址,这样ViewRootImpl里面就持有了Native层的Surface对象了,并且拥有了数据和信息。
2.drawSoftware()方法;
![](https://img-blog.csdnimg.cn/img_convert/2ac0068e2f326815f83977f19b2851e8.png)
看着好像挺简单的,我们来看一下lock和unlock到底做了什么事情,首先看 lockCanvas
![](https://img-blog.csdnimg.cn/img_convert/2dd3c64e8ace87865fd93a14f8ab41d0.png)
这里多了两个参数,一个mNativeObject,一个 mCanvas,看一下是什么
看一下mNativeObject:
![](https://img-blog.csdnimg.cn/img_convert/0730996bf35cbff9a8767e83457b3fd0.png)
mNativeObject上面说了是个内存地址
看一下mCanvas;
![](https://img-blog.csdnimg.cn/img_convert/65d91cedcbbcac9f10f1bf6f5f4adf84.png)
![](https://img-blog.csdnimg.cn/img_convert/cbe67cfd67c5b63657098087907bb3e1.png)
mCanvas就是自己重新new了一个空白的没有内存指向的一个画布
![](https://img-blog.csdnimg.cn/img_convert/30c646495633d743ca29e95cb0229700.png)
Java层的函数到这就没了,跟一下native层 nativeLockCanvas
![](https://img-blog.csdnimg.cn/img_convert/5abc785ebbffcf1cff4ebb376f5ac311.png)
这个函数还是相对简单的,主要干的事:通过传进去的dirtyRectObj先获得一块存储区域,然后将它和之前创建的空白的没有指定存储区域的mCanvas绑定到一起,最终返回这块画布,这样UI绘画的结果就记录在这块区域里面了。
再看一下unlockCanvasAndPost
![](https://img-blog.csdnimg.cn/img_convert/d60fc08a08cff4b9c88b5434d693fb68.png)
![](https://img-blog.csdnimg.cn/img_convert/060dea6ae1f1b5516874d697b3cb5646.png)
![](https://img-blog.csdnimg.cn/img_convert/e845001208b775b1ffa958dd25a92c87.png)
然后再看一下native层的代码
![](https://img-blog.csdnimg.cn/img_convert/b926442859a2fb809ec3b7932f96ee03.png)
ViewRootImpl里面关于Surface的东西就已经说完了
最后总结一下这一段干的事情,ViewRootImpl创建的时候调用了Surface的无参构造方法,缓存并初始化了一个surface对象,在每次relayoutWindow通过Session的代理对象mWindowSession进行跨进程通信,在WMS里面通过JNI在native层创建了保存了各种信息和数据的SurfaceControl对象,然后用ViewRootImpl里面存储的surface将这个SurfaceControl通过JNI传回到native层,从中获取到带有数据信息的新的Surface对象,将地址返回,然后赋值给ViewRootImpl中的mSurface对象,至此ViewRootImpl中就持有了native层的surface对象。
持有了这个surface对象后就开始了draw操作,在每次onDraw的时候首先通过JNI将一块空白的画布和绘制区域传到native层,然后通过绘制区域在native申请存储空间,将存储空间传给SKBitmap,再将SKBitmap 实例设置到之前传入的空白画布上,然后将有存储空间的canvas返回,绘制完成之后,再将这个绘制好的canvas从surface上解绑,然后再将canvas上的数据保存到底层。
![](https://img-blog.csdnimg.cn/img_convert/443587542c5b08054877afaec55d21e5.png)
遗留问题
这次主要是以Surface为主线任务,跟踪从Activity创建开始,Surface和应用之间的关系以及它做的一些事情,中间其实遗留了很多支线任务没有拓展,比如ViewRootImpl的WMS的关系,以及他们之间跨进程通信;Surface在native层具体的实现和运转逻辑SurfaceSession,SurfaceFlingerd,如何创建,如何保存Canvas数据以及如何获取存储空间的等等.