surfaceView
分析SurfaceView是怎么实现的,它继承自View,但是它可以在非UI线程完成界面的绘制。
1、绘制层的创建
创建时从ViewRoot的performTraversals开始的,在窗口需要刷新UI,就会调用。如果发现没有当前窗口的绘制层还没有创建,或者已经失效了,则或请求WMS服务创建新的绘制图层,并且同时会给SurfaceView创建自己的绘制层。

performTraversals中关于图层绘制的涉及到了2个方法的调用:dispatchAttachedToWindow:在判断了当前窗口是第一次被刷新UI时,触发这个调用,它的作用是从当前窗口的顶层视图开始,通知每一个子视图它要附加到宿主窗口上去了;dispatchWindowVisibilityChanged:在判断当前窗口的可见性发生了变化,触发这个调用,作用是通知每一个子视图它的宿主窗口发生了变化。
ViewGroup.dispatchAttachedToWindow这个并没有太多的逻辑,只是将调用分发到下面的View上去而已View.dispatchAttachedToWindow,它主要是完成2件事:- 首先是将窗口的相关信息的对象
AttachInfo保存到成员变量中; - 调用
onAttachedToWindow
- 首先是将窗口的相关信息的对象
SurfaceView.onAttachedToWindow,2件事:- 通知父视图,当前正在处理的SurfaceView需要在宿主窗口的绘图表面上挖一个洞,即需要在宿主窗口的绘图表面上设置一块透明区域。
- 调用从父类View继承下来的成员函数getWindowSession来获得一个实现了IWindowSession接口的Binder代理对象,并且将该Binder代理对象保存在SurfaceView类的成员变量mSession中。这是一个Binder的机制,通过这个Binder代理对象,完成和WMS的通信,请求WMS为自己创建绘图层。
ViewGroup.dispatchWindowVisibilityChanged分发View.dispatchWindowVisibilityChanged调用到onWindowVisibilityChangedSurfaceView.onWindowVisibilityChanged走到updateWindow上:mSurface这个就是SurfaceView的绘图层,是一个Surface对象;- SurfaceView所在图层可以设置,如
setZOrderOnTop() - 在SurfaceView还没有增加到WindowManagerService服务中去,调用WMS完成图层的创建并添加。
2、挖洞
由于SurfaceView的图层是小于宿主窗口(TYPE_APPLICATION_PANEL)的,那么SurfaceView就被宿主窗口覆盖住,所以需要在宿主窗口上挖个洞,将SurfaceView显示出来。

挖洞的实质是讲上层的图层透明化。
3、绘制
SurfaceView的绘制跟普通的View绘制时一样的,不同的是SurfaceView是在自己独立的图层上,不与宿主窗口共享一个绘制图层,这样就保证了SurfaceView即可以在UI线程绘制,也可以在非UI线程绘制。
虽然SurfaceView是独立的图层,但是还是会参与到宿主窗口的绘制中,即视图的draw()和dispatchDraw()都要被View流程调用到。
不同的是SurfaceView不在这里完成图像的绘制,在这里只是将图层染成了黑色而已~~~
具体的绘制时通过SurfaceHolder来实现的。

示例代码:
@Override
public void surfaceCreated(final SurfaceHolder holder) {
Log.e("1111", Thread.currentThread().getName());
// SurfaceHolder holder = surfaceView.getHolder();
Canvas canvas = holder.lockCanvas();
// start do draw on this canvas
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawCircle(getWindowManager().getDefaultDisplay().getWidth() / 2, getWindowManager().getDefaultDisplay
().getHeight() / 2, 80, paint);
// end do draw
holder.unlockCanvasAndPost(canvas);
}
补 充:
如果要在一个绘图表面进行UI绘制,那么就顺序执行以下的操作:
- 在绘图表面的基础上建立一块画布,即获得一个Canvas对象。
- 利用Canvas类提供的绘图接口在前面获得的画布上绘制任意的UI。
- 将已经填充好了UI数据的画布缓冲区提交给SurfaceFlinger服务,以便SurfaceFlinger服务可以将它合成到屏幕上去。