Android的UI底层是用CPU绘图的还是GPU绘图的呢?以及surfaceview,window,普通view是如何实现的?

补充下描述,Android中的surfaceview以及其他的textview,image view在底层实现上究竟有什么区别?在自定义view时有个canvas接口,这个又和surface有什么关系?
关注者
201
被浏览
12,589

7 个回答

前几天提问的,没有人回答,自己查了下,现在自己把这个问题回答下。

首先,对于CPU与GPU绘制的问题,根据google的文档
developer.android.com/g
安卓有2种绘制模型:
一.软件绘制模型,这里由CPU主导绘图,视图按照以下2个步骤绘图。
  1. 让视图结构(view hierarchy)失效。
  2. 绘制整个视图结构。

当应用程序需要更新它的部分UI时,都会调用内容发生改变的View对象的invalidate()方法。无效(invalidation)消息请求会在View对象层次结构中传递,以便计算出需要重绘的屏幕区域(脏区)。然后,Android系统会在View层次结构中绘制所有的跟脏区相交的区域。但是,这种方法有两个缺点:

1. 绘制了不需要重绘的视图(与脏区域相交的区域)

2. 掩盖了一些应用的bug(由于会重绘与脏区域相交的区域)

注意:在View对象的属性发生变化时,如背景色或TextView对象中的文本等,Android系统会自动的调用该View对象的invalidate()方法。



二.硬件加速绘制模型,这里由GPU主导绘图,视图按照以下3个步骤绘图。
  1. 让视图结构失效。
  2. 记录和更新显示列表(Display List)。
  3. 绘制显示列表。

  这种模式下,Android系统依然会使用invalidate()方法和draw()方法来请求屏幕更新和展现View对象。但Android系统并不是立即执行绘制命令,而是首先把这些View的绘制函数作为绘制指令记录一个显示列表中,然后再读取显示列表中的绘制指令调用OpenGL相关函数完成实际绘制。另一个优化是,Android系统只需要针对由invalidate()方法调用所标记的View对象的脏区进行记录和更新显示列表。没有失效的View对象就简单重用先前显示列表记录的绘制指令来进行简单的重绘工作。

  使用显示列表的目的是,把视图的各种绘制函数翻译成绘制指令保存起来,对于没有发生改变的视图把原先保存的操作指令重新读取出来重放一次就可以了,提高了视图的显示速度。而对于需要重绘的View,则更新显示列表,然后再调用OpenGL完成绘制。

  在这种绘制模型下,我们不能依赖一个视图与脏区(dirty region)相交而导致它的draw()方法被自动调用,所以必须要手动调用该视图的invalidate()方法去更新显示列表。如果忘记这么做可能导致视图在改变后不会发生变化。


硬件加速提高了Android系统显示和刷新的速度,但它也不是万能的,它有三个缺陷:

1. 兼容性(部分绘制函数不支持或不完全硬件加速)

2. 内存消耗(OpenGL API调用就会占用8MB,而实际上会占用更多内存)

3. 电量消耗(GPU耗电)


第二个问题,关于SurfaceView,View,Canvas等问题,


参考安卓framework开发者Dianne Hackborn在stackoverflow给出的定义,

stackoverflow.com/quest

以及相关的PPT,下载链接如下:

sites.google.com/site/a

首先亮图,然后再给出定义。

  1. 表面(Surface): Surface就是指向显存的一个物体,用来被绘制到屏幕上,所有你能看见的Window都拥有可以在上面绘制的Surface,在安卓中,系统使用Surface Flinger服务来把Surface按照正确的深度信息渲染到最终的屏幕上。一个Surface一般拥有超过一个(通常是2个) buffer 单元(back buffer 与 front buffer),系统通常在back buffer中进行渲染,完成之后与front buffer交换,这样显示到屏幕上,以实现流畅显示的效果。
  2. 窗口(Window): Window拥有一个Surface,在Surface里绘制Window里的内容。一个application通过Windows Manager来创建窗口,Windows Manager为每一个窗口创建Surface来让application在上面绘制各种物体。
  3. 视图(View):视图就窗口里的UI元素,一个窗口只拥有一个View Hierarchy,这些View Hierarchy提供了窗口里的所有表现。当一个窗口需要重新绘制时(比如一个View invalidate自己),锁定Surface,并返回一个Canvas用来在上面绘制,如上图所示,在view hierarchy树向下传递Canvas,来绘制每个view。这一切都完成后,Surface被解锁,并通过Surface Flinger交换前后Buffer来显示到屏幕上。
  4. Canvas:Canvas是Surface绘图时返回的一个接口,并提供一些绘图api,用来进行实际的绘图操作。目前Canvas可以绘制在bitmap或者openGL container上。
  5. SurfaceView: SurfaceView是View的一个特殊子类,它拥有专有的Surface,使application可以直接在上面绘制(普通的view hierarchy必须共享窗口唯一的surface)。这其中的原理很简单,SurfaceView就是请求Window Manager创建一个新窗口,并改变窗口之间的深度信息来显示。如果SurfaceView的Window显示在主窗口的后面,surfaceview将主窗口相应的位置设置成透明来使可见。

总结一下,一个activity拥有一个window(用来绘制它的UI), 并且一个Window只有一个Surface和View hierarchy来绘制,SurfaceView实质上是创建了一个新的窗口,所以拥有自己独立的Surface,可以直接绘制在上面。

恰好前几天总结了一张流程图,如下: