本文为作者原创,转载请注明出处。公众号为 毛铜飞 欢迎关注
(源码截图是Android 5.1.1_r6)
一 App和Surface的关系是怎样的
不论是用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的地方。
然后接着看Activity创建的代码performLaunchActivity
Instrumentation.java
跳转到Activity attach中
Window是个抽象类,那当前Activity中的这个mWindow的具体实现类是哪个?
继续跟这个PolicyManager
这里获取了return 了 com.android.internal.policy.impl.Policy 的这个类 makeNewWindow(context)方法,继续跟Policy这个类
这里发现了,Activity中存在的Window是PhoneWindow。
然后继续Activity创建好后的方法handleResumeActivity();
这里三个方法都要跟,先看第一个r.getWindow.getDecorView();
r是ActivityClinetRecord 是Activity描述的类,对Activity进行了高度抽象,记录了Activity的各种信息,大家应该都清楚,这里就不再赘述了。
1.window.getDecorView。
ActivityClinetRecord记录着是是Activity的信息,所以这个window就是刚刚说的Activity里面的PhoneWindow,我们跟一下PhoneWindow这个方法
再看一下installDecor()方法,这个方法是不是有点眼熟,setContentView就是调的这个,我们看一下SetContentView
再跟一下这个installDecor
如果用户没有传这个layout,mContentParent就是decorView的子布局
再看一下generateLayout(mDecor)这个方法,方法太多截取了一下
再看一下这个findViewById
通过这个其实就能看出,Activity的onCreate函数中,通过setContentView设置的View其实只是DecorView的子View,DecorView还处理了标题栏显示等一系列的工作
2. 第二个方法 :ViewManager wm =a.getWindowManager()
这个WM的具体实现类是什么?
a是Activity,Activity的getWindowManager方法看一下
这个mWindowManager上面跟过了,是从mWindow里面拿出来的,mWindow的类型是PhoneWindow之前也跟过了,想要知道这个wm是什么类型的,继续跟PhoneWindow的setWindowManager方法
发现里面没有这个方法,PhoneWindow继承Window看看父类
这里有个小细节,4.2以下和4.2及以上的实现不太一样,下面是4.1的Window的源码
看一下这个LocalWindowManager是什么
LoaclWindowManager是Window的一个内部类,LocalWindowManager可以算个代理类,里面很多方法的具体实现还是在WindowManagerImpl。从 Android 4.2开始就已经没有这个东西了
下面是4.2及以上的代码,返回的只是WindowManagerImpl这个类的实例
这里就已经知道了Activity内缓存的WindowManager类型(4.2之前是LocalWindowManager,4.2及4.2之后是WindowManagerImpl)
这里可以顺便把之前Window 和View的关系理一下
3.这里知道wm的具体实现类型了,就可以跟最后一个方法了,wm.addView()。
其实4.2以下的LocalWindowManager也是调用的super.addView(),所以这里统一看WindowManagerImpl 中的addView(decor,l),这里也有个小细节,4.2及4.2版本以上引入了一个新的WindowManagerGlobal概念,这里以5.1的源码为准
提供与系统窗口管理器不需要上下文的低级别通信,看WindowManagerGlobal的addView方法
然后再看一下ViewRootImpl
好了,终于是发现Surface的踪迹了。看一下这个Surface的无参构造方法
里面什么都没做,只是创建了一个空的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方法
先看到这个mWindowSession,他的实际类型是IWindowSession,看到这个大家应该就都明白咋回事了,这个是Session代理对象,就是WMS里面的那个成员变量mSessions里面装着的东西,如下图WindowManagerService的成员变量
Session继承于IWindowSession.Stub, 作为Binder服务端,同一进程中所有的ViewRootImpl对象只对应唯一相同的Session代理对象,所以这个addToDisplay()的函数是一个跨进程的调用,具体实现在Session类里,这里是ViewRootImpl和WMS的跨进程通信,和主题没什么关系,先不管它。
先继续跟requestLayout();
子线程不能刷新UI就是这个方法,ViewRootImpl里面有很多地方都调用了这个方法,这里就不一一贴了,由于本篇最之前看了,他是在onResume里面才被创建的,所以在某些极端情况onCreate里面在子线程刷新也是能成功的。
这边贴一个网图,就不再贴代码了,这里面大家应该都非常熟悉,主要工作是以DecorView为父容器开始自上而下的measure、layout、draw,measure确定View的测量宽和高,layout确定View的最终宽/高和四个顶点位置,draw将View绘制到屏幕上。
这个方法里面有两个地方和Surface相关,一个是relayoutWindow(),一个是drawSoftware()。看一下这两个方法
1. relayoutWindow()方法
其他参数可以先不用管,只要知道最后的mSuface就是ViewRootImpl 成员变量里面new出来的空的surface对象。
mWindowSession上面讲了,实际处理在Session的relayout里面,看一下代码
这个mService是WMS,再看一下WMS的 relayoutWindow方法
先跟一下winAnimator的createSurfaceLocked,
继续跟
mNativeObject我的理解是native端返回的内存地址,如果为0说明对应的对象没有创建成功或者异常了。
继续跟native代码
这里主要做的事情是通过SurfaceControl的构造方法,把所有的信息和数据,都传到native层,在native层创建了一个SurfaceControl的对象,并将地址返回了。
SurfaceControl类可以看作是一个wrapper类:它对一些公开函数封装了一层,里面还有些Layer相关的东西暂且先不管它们。
再看一下Surface的copyFrom方法
继续跟native 层的创建
这边将之前native层创建的包含了所有信息和数据的SurfaceControl传到native层,通过这个control,在底层创建了一个含有真实数据信息的Surface,并将地址返回了,再看java层的 setNativeObjectLocked方法
这个方法就是把本地的ViewRootImpl 中的mSurface的内存地址替换了,换成了native层的地址,这样ViewRootImpl里面就持有了Native层的Surface对象了,并且拥有了数据和信息。
2.drawSoftware()方法;
看着好像挺简单的,我们来看一下lock和unlock到底做了什么事情,首先看 lockCanvas
这里多了两个参数,一个mNativeObject,一个 mCanvas,看一下是什么
看一下mNativeObject:
mNativeObject上面说了是个内存地址
看一下mCanvas;
mCanvas就是自己重新new了一个空白的没有内存指向的一个画布
Java层的函数到这就没了,跟一下native层 nativeLockCanvas
这个函数还是相对简单的,主要干的事:通过传进去的dirtyRectObj先获得一块存储区域,然后将它和之前创建的空白的没有指定存储区域的mCanvas绑定到一起,最终返回这块画布,这样UI绘画的结果就记录在这块区域里面了。
然后再看一下native层的代码
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上的数据保存到底层。
遗留问题
这次主要是以Surface为主线任务,跟踪从Activity创建开始,Surface和应用之间的关系以及它做的一些事情,中间其实遗留了很多支线任务没有拓展,比如ViewRootImpl的WMS的关系,以及他们之间跨进程通信;Surface在native层具体的实现和运转逻辑SurfaceSession,SurfaceFlingerd,如何创建,如何保存Canvas数据以及如何获取存储空间的等等.
来源:CSDN
作者:mao7101
链接:https://blog.csdn.net/mao7101/article/details/84066004