基于 android-27源码
https://blog.csdn.net/Luoshengyang/article/details/8661317
1. 什么是SurfaceView,和普通的View的区别?
- View适用于主动更新的情况,而SurfaceView则适用于被动更新的情况,比如频繁刷新界面。
- View在主线程中对页面进行刷新,而SurfaceView则开启一个子线程来对页面进行刷新。
- View在绘图时没有实现双缓冲机制,SurfaceView在底层机制中就实现了双缓冲机制。(当一个动画争先显示时,程序又在改变它,前面还没有显示完,程序又请求重新绘制,这样屏幕就会不停地闪烁。而双缓冲技术是把要处理的图片在内存中处理好之后,再将其显示在屏幕上。双缓冲主要是为了解决 反复局部刷屏带来的闪烁。把要画的东西先画到一个内存区域里,然后整体的一次性画出来。)
2. SurfaceView的绘制原理
Android应用程序窗口是通过SurfaceFlinger服务来绘制自己的UI。一般来说,每一个窗口在SurfaceFlinger服务中都对应有一个Layer,用来描述它的绘图表面。对于那些具有SurfaceView的窗口来说,每一个SurfaceView在SurfaceFlinger服务中还对应有一个独立的Layer或者LayerBuffer,用来单独描述它的绘图表面,以区别于它的宿主窗口的绘图表面。
3. SurfaceView的绘制过程
SurfaceView的绘图表面的创建过程从ViewRoot类的成员函数performTraversals开始
3.1 ViewRoot.performTraversals
host = ViewRoot.mView 指向DecorView对象,描述当前窗口的顶级视图
attachInfo = ViewRoot.mAttachInfo 指向的AttachInfo是描述串口信息对象
这个方法的主要作用是判断绘图表面是否创建,通知View(ViewGroup)附加到窗口 3.2 ,判断并通知当前窗口的可见性是否变化 3.5
3.2 ViewGroup.dispatchAttachedToWindow() (DecorView ViewGroup)
遍历通知子视图添加到窗口 3.3
3.3 View.dispatchAttachedToWindow() (View)
保存窗口信息mAttachInfo,调用子类的onAttachedToWindow 3.4
3.4 SurfaceView.onAttachedToWindow() (SurfaceView)
主要做了两件事:
1 通知父视图,当前正在处理的SurfaceView需要在宿主窗口的绘图表面上挖一个洞,即需要在宿主窗口的绘图表面上设置一块透明区域。 待更新
2 调用从父类View继承下来的成员函数getWindowSession()来获得一个实现了IWindowSession接口的Binder代理对象. mSession指向这个对象.主要是想通过binder请求绘制绘图表面.
3.5 ViewGroup.dispatchWindowVisibilityChanged() (DecorView ViewGroup)
遍历设置子View的可见性 3.6
3.6 View.dispatchWindowVisibilityChanged() (View)
调用成员函数onWindowVisibilityChanged()来让子类处理可见性变化
3.7 SurfaceView.onWindowVisibilityChanged() (SurfaceView)
设置当前SurfaceView的可见性,调用3.8方法,更新视图,如果还没有绘制SurfaceView,就请求绘制
3.8 SurfaceView.updateWindow
重要的成员变量解释:
mSurface:指向特定的绘图表面,其他的View的绘图表面是共享的,SurfaceView的是特有的;
mWindow:指向MyWindow对象,每个SurfaceView都关联了一个实现了IWindow接口的Binder本地对象
mWindowType:描述SurfaceView的窗口类型,默认是显示多媒体的类型,可通过窗口设置层级,比如media的在下面,media_overlate的在上面;也可以通过修改setZXXX()的值来提升在Z轴的显示层级
mRequestedType:绘图表面类型 Layer or LayerBuffer 对应的在SurfaceFlinger的内存分配也不一致.
3.8.1 绘图表面的创建过程:
判断并准备宿主窗口
获得SurfaceView宽高
-
更新记录SurfaceView的绘制信息,可见性、位置、大小、绘图表面像素格式和类型等等
检查成员变量mWindow的值是否等于null,相当于检测是否添加到WindowManagerService服务
调用成员变量mSession所描述的一个Binder代理对象的成员函数relayout来请求WindowManagerService服务对SurfaceView的UI进行布局
3.8.2 SurfaceView的挖洞过程:
- SurfaceView -- SurfaceView.onAttachedToWindow
调用mParent.requestTransparentRegion(SurfaceView.this);来请求在宿主窗口挖洞; - ViewGroup -- requestTransparentRegion()
设置标志位mPrivateFlags为顶层绘制透明窗口,调用mParent(ViewRoot)的方法; - ViewRootImp -- requestTransparentRegion()
检查线程(为非主线程),检查viewRoot指向的对象和传入的参数是否是同个对象;设置标志位,调requestLayout()开始刷新窗口; - ViewRootImp -- performTraversals()
在窗口的UI布局完成之后,并且在窗口的UI绘制之前,收集嵌入在它里面的SurfaceView所设置的透明区域的,这样子View的大小和位置才能确定; -
ViewGroup -- gatherTransparentRegion() 挖洞过程
5.1 调用父类View的成员函数gatherTransparentRegion来检查当前正在处理的视图容器是否需要绘制。
5.2 遍历子类的gatherTransparentRegion来继续往下收集透明区域。 -
SurfaceView -- gatherTransparentRegion()
假设当前正在处理的SurfaceView不是用作窗口面板,并且也是不需要在宿主窗口的绘图表面上进行绘制的,而参数region的值又不等于null,那么SurfaceView类的成员函数gatherTransparentRegion就会先计算好当前正在处理的SurfaceView所占据的区域,然后再将该区域添加到参数region所描述的区域中去,这样就可以得到窗口的一个新的透明区域。
3.8.3 SurfaceView的绘制过程:
如果要在一个绘图表面进行UI绘制,那么就顺序执行以下的操作:
(1). 在绘图表面的基础上建立一块画布,即获得一个Canvas对象。
(2). 利用Canvas类提供的绘图接口在前面获得的画布上绘制任意的UI。
(3). 将已经填充好了UI数据的画布缓冲区提交给SurfaceFlinger服务,以便SurfaceFlinger服务可以将它合成到屏幕上去。
SurfaceView提供了一个SurfaceHolder接口,通过这个SurfaceHolder接口就可以执行第(1)和引(3)个操作;
SurfaceView sv = (SurfaceView )findViewById(R.id.surface_view);
SurfaceHolder sh = sv.getHolder();
Cavas canvas = sh.lockCanvas()
//Draw something on canvas
......
sh.unlockCanvasAndPost(canvas);
- SurfaceView.getHolder
获得SurfaceHolder对象 - SurfaceHolder.lockCanvas
因为SurfaceView是在子线程执行绘制的,画布的绘制过程不是线程安全的,所以在绘制的时候需要对当前的绘图表面进行锁保护--mSurfaceLock; - Surface.lockCanvas
通过JNI方法来在当前正在处理的绘图表面上获得一个图形缓冲区,并且将这个图形绘冲区封装在一块类型为Canvas的画布中返回给调用者使用。
mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
这样就可以开始在mCanvas中绘制
- SurfaceHolder.unlockCanvasAndPost
SurfaceHolder类的成员函数unlockCanvasAndPost再调用当前正在处理的SurfaceView的成员变量mSurfaceLock所指向的一个ReentrantLock对象的成员函数unlock来解锁当前正在处理的SurfaceView的绘图表面 - Surface.unlockCanvasAndPost
mHwuiContext.unlockAndPost(canvas);
将在前面的Step 3中所获得的一个图形缓冲区提交给SurfaceFlinger服务,以便SurfaceFlinger服务可以在合适的时候将该图形缓冲区合成到屏幕上去显示,这样就可以将对应的SurfaceView的UI展现出来了;绘制后释放锁
总结:
SurfaceView有以下三个特点:
A. 具有独立的绘图表面;
B. 需要在宿主窗口上挖一个洞来显示自己;
C. 它的UI绘制可以在独立的线程中进行,这样就可以进行复杂的UI绘制,并且不会影响应用程序的主线程响应用户输入。