总体关系
Surface是Android中的图像系统,我们将通过以下两点来对它进行学习:
应用程序和Surface的关系
Surface和SurfaceFlinger之间的关系
两个图表明了这两点:
左图表示了应用程序和Surface之间的关系,可以发现,不论是使用Skia绘制二维图像,还是用OpenGL绘制三维图像,最终Application都要和Surface交互。Surface就像是UI的画布。而App则像是在Surface上作画。
右图表示了Surface和SurfaceFlinger的关系。Surface向SurfaceFlinger提供数据,而SurfaceFlinger则混合数据。
Activity的显示
一般来说,应用程序的外表是通过Activity来展示的。那么,Activity是如何完成界面绘制工作的呢?根据前面所讲的知识可知,应用程序的显示和Surface有关,那么具体到Activity上,它和Surface又是什么关系?
Activity的创建
在收到创建Activity请求后,zygote进程在响应请求后会for一个子进程,这个子进程是App对应的进程,它的入口函数是ActivityThread类的main函数。
main函数中也包括了初始化主线程(UI线程)Looper的逻辑
ActivityThread类中有一个handleLaunchActivity函数,它就是创建Activity的地方,代码如下所示:
1 | private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) { |
handleLaunchActivity函数中列出了两个关键点:
perforLaunchActivity
handleResumeActivity
创建Activity
第一个关键函数performLaunchActivity返回一个Activity,这个Activity就是App中的那个Activity(仅仅考虑App中只有一个Activity的情况),它的代码如下:
1 | private final Activity performLaunchActivity(ActivityRecord r,Intent cusomIntent) { |
performLaunchActivity的作用有以下两点:
根据类名以Java反射的方法创建一个Activity
调用Activity的onCreate函数
在onCreate函数中,我们一般会调用setContentView函数,代码如下所示:
1 | public void setContentView(View view) { |
下面来看一下window和view的两个概念:
Window:Window是一个抽象基类,用于控制顶层窗口的外观和行为,它的作用是绘制背景和标题栏、默认的按键处理等。它将作为一个顶级的View加入到WindowManager中。
View:View是一个基本的UI单元,占据屏幕的一块矩形区域,可用于绘制,并能处理事件。
那么Window是一个抽象类,它实际的对象是什么类型的?Window Manager究竟是什么?
Windows实际的创建在这段代码里面:
1 | //利用PolicyManager来创建Window对象 |
PolicyManager的代码如下:
1 | public final class PolicyManger{ |
这里有一个单例的sPolicy对象,它是Policy类型的,定义如下:
1 | public class Policy implements IPolicy { |
在这里就能确定了,mWindow对象实际上是一个PhoneWindow,而WindowManger的实际创建代码如下:
1 | wm = WindowManager.getDefault(); |
LocalWindowManager是在Window中定义的内部类,请看它的构造函数,定义如下:
1 | private class LocalWindowManager implements WindowManager { |
wm的实际类型为WindowManagerImpl,他们的类关系如下:
从这个图可以得出以下结论:
Activity的mWindow成员变量其真实类型是PhoneWindow,而mWindowManager成员变量的真实类型是LocalWindowManager。
LocalWindowManager和WindowManagerImpl都实现了WindowManager接口。这里采用的是Proxy模式,表明LocalWindowManager将把它的工作委托给WindowManagerImpl来完成。
Activity的setContent函数实际上调用的是PhoneWindow的setContent函数,PhoneWindow的setContent函数如下所示:
1 | public void setContentView(View view) { |
installDecor函数如下:
1 | private void installDecor() { |
而generateLayout的作用就是获取对应的标题栏,然后将它加入到ViewGroup中,最后将ViewGroup返回。
PhoneWindow、WindowManager、DecorView的关系如下:
从上图可以看出,在Activity的onCreate函数中,通过setContentView设置的View,其实只是DecorView的子View。DecorView还处理了标题栏显示等一系列的工作。
handleResumeActivity分析
1 | final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { |
在上文中,我们分析了DecorView和ViewManager等对象,那么最后的wm.addView(decor, I)是什么呢?实际上,这里创建了一个ViewRoot对象,并把刚刚的View设置到这个ViewRoot对象中。
- ViewRoot是什么?
ViewRoot实现了ViewParent接口,但是它不处理绘画,因为它没有onDraw函数。
ViewRoot的定义如下:
1 | public final class ViewRoot extends Handler implements ViewParent,View.Attachinfo.Callbacks { |
这个定义表达了三点:
ViewRoot继承了Handler类,看来它能处理消息。ViewRoot重写了handleMessage函数。
ViewRoot有一个成员变量叫mSurface,它是Surface类型
ViewRoot还有W类型的mWindow和一个View类型的mView变量
其中,W是ViewRoot定义的一个静态内部类:
1 | static class W extends IWindow.Stub |
这个类将参与Binder的通信。
其中,Surface是Android真正的画布,它包含了以下几点:
有一块Raw buffer,至于是内存还是显存,不必管它。
Surface操作这块Raw buffer
Screen compositor(其实就是SurfaceFlinger)管理这块Raw buffer
Surface和SF、ViewRoot的关系图如下:
这张图表达了以下几点:
ViewRoot有一个成员变量mSurface,它是Surface类型,它和一块RawBuffer有关联。
ViewRoot是一个ViewParent,它的子View的绘画操作,是在画布Surface上展开的。
Surface和SurfaceFlinger有交互
ViewRoot的构造如下:
1 | public ViewRoot(Context context) { |
getWindowsession函数,将建立Activity的ViewRoot和WindowManagerService的关系。代码如下所示:
1 | ublic static IWindowSession getWindowSession(Looper mainLooper) { |
WindowManagerService由System_Server进程启动,SurfaceFligner也在这个进程中,Activity的显示还需要与WMS建立联系。我们先看setView的处理。
ViewRoot的setView函数做了三件事:
保存传入的view参数为mView,这个mView指向PhoneWindow的DecorView。
调用requestLayout
调用IWindowSession的add函数,这是一个跨进程的Binder通信,第一个参数是mWindow,它是W类型,从IWindow.stub派生。
可以看出,ViewRoot和远端进程SystemServer的WMS有交互,先来总结以下它和WMS的交互流程。
ViewRoot调用openSession,得到一个IWindowSession对象
调用WindowSession对象的add函数,把一个W类型的mWindow对象作为参数传入
- ViewRoot和WMS的关系
上面总结了ViewRoot和WMS的交互流程,其中一共有两个跨进程的调用,ViewRoot和WMS之间的关系如下:
这张图中的知识点如下:
ViewRoot通过IWindowSession和WMS进程进行跨进程通信。IWindowSession定义在IWindowSession.aidl文件中。这个文件在编译由aidl工具处理,最后会生成类似于Native Binder中Bn端和Bp端的代码。每个App进程都会和WMS建立一个IWindowSession对话,这个会话会被App进程用于和WMS通信。
ViewRoot内部有一个W类型的对象,它也是一个基于Binder通信的类,W是IWindow的Bn端,用于响应请求。IWindow定义在另一个aidl文件IWindow.aidl中。IWindow主要用于Android案件事件的分发,当WMS所在的SystemServer进程接收到按键事件后,会把它交给这个IWindow对象。
Activity的UI绘制
在ViewRoot的setView函数中有一个requestLayout函数,它会向ViewRoot发送一个DO_TRAVERSAL消息,handleMessage函数中,对这个消息的处理是调用performTraversal函数,在performTraversal函数中,它会调用relayoutWindow和draw,这两个函数的作用分别如下:
- relayWindow
relayoutWindow中会调用IWindowSession的relayout函数。
- draw
在mSurface中lock一块Canvas,调用DecorView的draw函数,canvas就是画布,最后unlock画布,屏幕上马上就会显示了。
Activity的总结
Activity的创建和显示,可以提炼成如下几条:
Activity的顶层View是DecorView,而我们在onCreate函数中通过setContentView设置的View只不过是这个DecorView中的一部分罢了。DecorView是一个FrameLayout类型的ViewGroup。
Activity和UI有关,它包括一个Window(真实类型是PhoneWindow)和一个WindowManager(真实类型是LocalWindowManager)对象。这两个对象将控制整个Activity的显示。
LocalWindowManager使用了WindowManagerImpl作为最终的处理对象(Proxy模式),这个WindowManagerImpl中有一个ViewRoot对象。
ViewRoot实现了ViewParent接口,它有两个重要的成员变量,一个是mView,它指向Activity顶层UI单元的DecorView,另外一个是mSurface,这个Surface包含了一个Canvas(画布)。除此之外,ViewRoot还通过Binder系统和WindowManagerService进行了跨进程通信。
ViewRoot能处理Handler的消息,Activity的显示就是由ViewRoot在它的performTraversals函数中完成的
整个Activity的绘图流程就是从mSurface中lock一块Canvas,然后交给mView去自由发挥画画的才能,最后unLockCanvasAndPost释放这块Canvas
初识Surface
Surface对象是纵跨Java/JNI层的对象,与它相关的流程如下:
- 在ViewRoot构造时,会创建一个Surface,它使用无参构造函数,代码如下:
1 | private final Surface mSurface = new Surface(); |
- ViewRoot通过IWindowSession和WMS交互,而WMS调用中调用的一个attach函数会构造一个SurfaceSession,代码如下:
1 | void windowAddedLocked() { |
ViewRoot在performTransval的处理过程中会调用IWindowSession的relayout函数。
ViewRoot调用Surfaced lockCanvas,得到一块画布
ViewRoot调用Surface的unLockCanvasAndPost释放这块画布。
下面来看一下relayout函数的本质,relayout函数是一个跨进程的调用,由WMS完成实际处理。
Surface的创建流程如下:
来自CSDN邓老师博客
由此图可以看出:
WMS中的Surface才是真正的Surface,它的构造使用了带SurfaceSession参数的构造函数
ViewRoot中的Surface是一个“伪”Surface,它的构造使用了无参构造函数。
copyFrom就是进行转移,它将真正的Surface信息拷贝到“伪”Surface即outSurface里
而这个过程则需要使用aidl帮助完成,这个过程发生在ViewRoot调用IWindowSession的relayout函数中,它在IWindowSession.aidl的定义如下:
创建Surface的JNI层分析
在JNI层,第一个被调用的是Surface的无参构造函数,其代码如下所示:
1 | public Surface() { |
Canvas是画图所需要的四个类其中之一,这四个类分别是:
Bitmap:用于存储像素,也就是画布。可把它当作一块数据存储区域。
Canvas:用于记载画图的动作,比如画一个圆,画一个矩形等。Canvas类提供了这些基本的绘图函数。
Drawing primitive:绘图基本元素,例如矩形、圆、弧线、文本、图片等。
Paint:它用来描述绘画时使用的颜色、风格(如实线、虚线等)
在一般情况下,Canvas会封装一块Bitmap,而作图就是基于这块Bitmap的。前面所说的画布,其实指的就是Canvas中的这块Bitmap。
那么,真正的Surface怎么对它进行替换呢?可以分为以下几个流程:
创建一个SurfaceComposerClient
调用SurfaceComposerClient的createSurface得到一个SurfaceControl对象
调用SurfaceControl的writeToParcel把一些信息写到Parcel包中
根据Parcel包的信息构造一个Surface对象。把这个Surface对象保存到Java层的mSurface对象中,这样,ViewRoot最终得到了一个Native的Surface对象。
Surface和画图
最后两个和Surface相关的函数调用是lockCanvas和unlockCanvasAndPost。
lockCanvas: 先获得一块存储区域,然后将它和Canvas绑定到一起,这样,UI绘画的结果就记录在这块存储里了
unlockCanvasAndPost:调用Surface对象的unlockAndPost函数
Surface流程
Surface精简流程可以由如下这个图概括
ViewRoot总结
ViewRoot是Surface系统甚至UI系统中一个非常重要的类,VieRoot的总结如下:
- ViewRoot和View类的关系是上面?
ViewRoot是View视图体系的根,每一个Window(注意是Window,比如PhoneWindow)有一个ViewRoot,它的作用是处理layout和View视图体系的绘制工作。视图体系包括Views和ViewGroupa,也就是SDK中能看到的View类都属于视图体系。根据前面的根系可知,这些View是需要通过draw画出来的。而ViewRoot就是用来draw他们的,ViewRoot本身没有draw/onDraw函数。
- ViewRoot和它所控制的View及其子View使用同一个Canvas吗?
ViewRoot提供Canvas给它所控制的View,所以他们使用同一个Canvas。但Canvas使用的内存却不是固定的,而是通过Surface的lockCanvas得到的。
- View、Surface和Canvas之间的关系?
一个Window将和一个Surface绑定在一起,绘制前ViewRoot会从Surface中lock除一个Canvas。
- Canvas有一个bitmap,那么绘制UI时,数据是画在Canvas的这个bitmap中吗?
是的,bitmap实际上包括了一块内存,绘制的数据最终都在这块内存上。
- 同一个ViewRoot下,不同类型的View使用同一个Surface吗?
是的,但是SurfaceView要除外。因为SurfaceView的绘制一般在独立的线程上,并且由应用层主动调用lockCanvas,draw和unlockCanvasAndPost来完成绘制流程。应用层相当于抛开了ViewRoot的控制直接和屏幕打交道,这在camera、video方面用的最多。
Surface总结
主要是这两点:
应用程序和Surface的关系
Surface和SurfaceFlinger的关系
应用程序与Surface的关系从应用程序的Activity开始,一路追踪到ViewRoot、WindowManagerService。
SurfaceFlinger则主要负责视图的显示,App 端 copyFrom() 出来 NativeSurface 时会创建一个 SharedBufferClient 与 SharedClient 这块共享内存关联。当客户端 addView() 或者需要更新 View 时,会通过 SharedBufferClient 写入数据到 ShareClient 中,SurfaceFlinger 中的 SharedBufferServer 接收到通知会将 FrameBuffer 中的数据传输到屏幕上。HWComposer 是基于硬件来产生 VSync 信号的,来通知 SurfaceFlinger 重绘控制显示的帧率。