Surface
- SurfaceView: View 的子类,但不与宿主 Window 共享 Surface, 而是有自己独立的 Surface, 且可以在一个独立的线程中进行绘制,因此 SurfaceView 一般用来实现比较复杂的图像或动画/视频的显示。可以参考 Android双缓存与SurfaceView。由于其内容是绘制在一个独立的 Surface 上,因此无法用 scrollTo/By 等方法去移动操作 Canvas 里的内容,但是可对整个 View 进行平移,缩放,旋转等变换操作。
- GLSurfaceView: 基于 SurfaceView 再次进行扩展,在 SurfaceView 基础上封装了 EGL 环境管理以及 Render 线程,专门为 OpenGl 显示渲染使用。参考 Android-OpenGL-ES笔记。
- TextrueView: Android 4.0 后引入 TextureView, 它将 SurfaceTexture 和 View 结合到了一起。与 SurfaceView 相比,它并没有创建一个单独的 Surface 来绘制,解决了 SurfaceView 无法在 Canvas 内容上做动画的问题。另外 TextureView 必须在硬件加速开启的窗口中使用。
View的完整工作流程,从cpu计算控件数据,到surfaceFlinger合成这些数据,再到vsync信号达到后开始绘制(见/ScreenDraw章);
Surface
Window的核心变量。
Android
系统采用一种称为 Surface
的图形架构,简而言之,每一个 Activity
都关联有至少一个 Window
(窗口),每一个 Window
都对应有一个 Surface
。
Surface
这里直译过来叫做 绘图表面 ,顾名思义,其可在内存中生成一个图形缓冲区队列,用于描述 UI
,经与系统服务的WindowServiceManager
通信后、通过 SurfaceFlinger
服务持续合成并送显到显示屏。
由此可见,通常情况下,一个 Activity
的 UI
渲染本质是 系统提供一块内存,并创建一个图形缓冲区进行维护;这块内存就是 Surface
,最终页面所有 View
的 UI
状态数据,都会被填充到同一个 Surface
中。
SurfaceView & TextureView
SurfaceView 本身的输出不是通过 Android 的 UI Renderer(HWUI),而是直接走系统的窗口合成器 SurfaceFlinger,
//暂时CV,待完善
1 Surface
Surface 就是“表面”的意思,可以简单理解为内存中的一段绘图缓冲区。在 SDK 的文档中,对 Surface 的描述是这样的:“Handle onto a raw buffer that is being managed by the screen compositor”,意思是“由屏幕显示内容合成器(screen compositor)所管理的原生缓冲器的句柄”, 这句话包括下面两个意思:
- 通过 Surface(因为 Surface 是句柄)就可以获得原生缓冲器以及其中的内容。就像在C语言中,可以通过一个文件的句柄,就可以获得文件的内容一样;
- 原生缓冲器(rawbuffer)是用于保存当前窗口的像素数据的。
简单的说 Surface 对应了一块屏幕缓冲区,每个 Window 对应一个 Surface,任何 View 都是画在 Surface 上的,传统的 view 共享一块屏幕缓冲区,所有的绘制必须在 UI 线程中进行。我们不能直接操作 Surface 实例,要通过 SurfaceHolder,在 SurfaceView 中可以通过 getHolder() 方法获取到 SurfaceHolder 实例。 Surface 是一个用来画图形的地方,但是我们知道画图都是在一个 Canvas 对象上面进行的,Surface 中的 Canvas 成员,是专门用于提供画图的地方,就像黑板一样,其中的原始缓冲区是用来保存数据的地方。 Surface 本身的作用类似一个句柄,得到了这个句柄就可以得到其中的Canvas、原始缓冲区以及其他方面的内容,所以简单的说 Surface 是用来管理数据的(句柄)。 说完 surface 就可以说 SurfaceView 了。
2 SurfaceView
2.1 SurfaceView 简介
简单的说 SurfaceView 就是一个有 Surface 的 View,SurfaceView 控制这个 Surface 的格式和尺寸以及绘制位置。传统 View 及其派生类的更新只能在 UI 线程,然而 UI 线程还同时处理其他交互逻辑,这就无法保证 view 更新的速度和帧率了,而 SurfaceView 可以用独立的线程来进行绘制。因此可以提供更高的帧率,例如游戏,摄像头取景等场景就比较适合用 SurfaceView 来实现。 SurfaceView 的核心在于提供了两个线程:UI线程和渲染线程,两个线程通过“双缓冲”机制来达到高效的界面刷新效果。
2.2 SurfaceView 实现机制
SurfaceView 继承自 View,所以它也是一个 View。但是这个 View 和普通的 View 有点不同。SurfaceView 有自己的 Surface,在 Android 中,一个 View 有自己的 Surface,在 WMS 中中就有对应的 WindowState,对应在 SurfaceFlinger 中就有 Layer。
一般的 Activity 包含的多个 View 会组成 View hierachy 的树形结构,只有最顶层的 DectorView 才是对 WMS 可见的,这个 DecorView 在 WMS 中有一个对应的 WindowState,相应的,在 SurfaceFlinger 中有对应的 Layer。而 SurfaceView 正因为它有自己的 Surface,有自己的 Window,它在 WMS 中有对应的 WindowState,在 SurfaceFlinger 中有 Layer。 虽然在 App 端它仍在 View hierachy 中,但在 Server 端(WMS 和 SurfaceFlinger)中,它与宿主窗口是分离的。这样的好处是对这个 Surface 的渲染可以放到单独的线程中去做,渲染时可以有自己的 GL context。这对于一些游戏、视频等性能相关的应用非常有益,因为它不会影响主线程对事件的响应。 但是这也有缺点,因为这个 Surface 不在 View hierachy 中,它的显示也不受 View 的属性控制,所以不能进行平移、缩放等动画,它也不能放在其它 ViewGroup 中,SurfaceView 不能嵌套使用,而且不能使用某些 View 的特性,例如 View.setAlpha()。 从 Android7.0 开始,SurfaceView 的窗口位置与其他 View 渲染同步更新。这意味着在屏幕上平移和缩放 SurfaceView 不会导致渲染失真。
3 TextureView
因为上面所说的 SurfaceView 不在主窗口中,它没法做动画没法使用一些 View 的特性方法,所以在 Android 4.0中引入了 TextureView,它是一个结合了 View 和 SurfaceTexture 的 View 对象。 TextureView 是一个可以把内容流作为外部纹理输出在上面的 View,和 SurfaceView 不同,它不会在 WMS 中单独创建窗口,而是作为 View hierachy 中的一个普通 view,因此它可以和其他普通 View 一样进行平移、旋转、缩放等动画。但是 TextureView 必须在硬件加速的窗口中,它显示的内容流数据可以来自 App 进程或者远程进程。 TextureView 继承自 View,它与其它的 View 一样在 View hierachy 中管理与绘制。TextureView 重载了 draw() 方法,其中主要 SurfaceTexture 中收到的图像数据作为纹理更新到对应的 HardwareLayer 中。 SurfaceTexture.OnFrameAvailableListener 用于通知 TextureView 内容流有新图像到来。SurfaceTextureListener 接口用于让 TextureView 的使用者知道 SurfaceTexture 已准备好,这样就可以把 SurfaceTexture 交给相应的内容源。 Surface 为 BufferQueue 的 Producer 接口实现类,使生产者可以通过它的软件或硬件渲染接口为 SurfaceTexture 内部的 BufferQueue 提供 graphic buffer。 SurfaceTexture 可以用作非直接输出的内容流,这样就提供二次处理的机会。与 SurfaceView 直接输出相比,这样会有若干帧的延迟。同时,由于它本身管理 BufferQueue,因此内存消耗也会稍微大一些。
4 SurfaceTexture
SurfaceTexture 是 Surface 和 OpenGL ES(GLES) 纹理的组合。SurfaceTexture 用于提供输出到 GLES 纹理的 Surface。 SurfaceTexture 是从 Android 3.0(API level 11)开始加入,与 SurfaceView 不同的是,它对图像流的处理并不直接显示,而是转为 GL 外部纹理,因此用于图像流数据的二次处理。 比如 Camera 的预览数据,变成纹理后可以交给 GLSurfaceView 直接显示,也可以通过 SurfaceTexture 交给TextureView 作为 View heirachy 中的一个硬件加速层来显示。 首先,SurfaceTexture 从图像流 (来自 Camera 预览、视频解码、GL 绘制场景等)中获得帧数据,当调用updateTexImage()时,根据内容流中最近的图像更新 SurfaceTexture 对应的 GL 纹理对象。 SurfaceTexture 包含一个应用是其使用方的 BufferQueue。当生产方将新的缓冲区排入队列时,onFrameAvailable() 回调会通知应用。然后,应用调用 updateTexImage(),这会释放先前占有的缓冲区,从队列中获取新缓冲区并执行 EGL 调用,从而使 GLES 可将此缓冲区作为外部纹理使用。
5 SurfaceView 与 TextureView 的对比
项目 | SurfaceView | TextureView |
---|---|---|
内存 | 低 | 高 |
耗电 | 低 | 高 |
绘制 | 及时 | 1-3帧延迟 |
动画和截图 | 不支持 | 支持 |
从性能和安全性角度出发,优先选 SurfaceView,TextureView 是一个不得已的选择:
- 在 Android 7.0 上系统 Surfaceview 的性能比 TextureView 更有优势,支持对象的内容位置和包含的应用内容同步更新,平移、缩放不会产生黑边。 在7.0以下系统如果使用场景有动画效果,可以选择性使用TextureView。
- 由于失效(invalidation)和缓冲的特性,TextureView 增加了额外1~3帧的延迟显示画面更新。
- TextureView 总是使用 GL 合成,而 SurfaceView 可以使用硬件 overlay 后端,可以占用更少的内存。
- TextureView 的内部缓冲队列导致比 SurfaceView 使用更多的内存。
- SurfaceView 内部自己持有 Surface,Surface 创建、销毁、大小改变时系统来处理的,通过 SurfaceHolder 的 callback 回调通知。
- 当画布创建好时,可以将 surface 绑定到 MediaPlayer 中。SurfaceView 如果为用户可见的时候,创建 SurfaceView 的 SurfaceHolder 用于显示视频流解析的帧图片,如果发现 SurfaceView 变为用户不可见的时候,则立即销毁 SurfaceView 的 SurfaceHolder,以达到节约系统资源的目的。
作者:ByteSaid
链接:https://juejin.cn/post/7156157715230752782
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
SurfaceFlinger
SurfaceFlinger 接受缓冲区,对它们进行合成,然后发送到屏幕。WindowManager 为 SurfaceFlinger 提供缓冲区和窗口元数据,而 SurfaceFlinger 可使用这些信息将 Surface 合成到屏幕。
SurfaceFlinger 用来管理消费当前可见的 Surface, 所有被渲染的可见 Surface 都会被 SurfaceFlinger 通过 WindowManager 提供的信息合成(使用 OpenGL 和 HardWare Composer)提交到屏幕的后缓冲区,等待屏幕的下一个 Vsync 信号到来,再显示到屏幕上。SufaceFlinger 通过屏幕后缓冲区与屏幕建立联系,同时通过 Surface 与上层建立联系,起到了一个承上启下的作用。
启动
在 SurfaceFlinger 的启动流程中:
- 首先会创建 SurfaceFlinger 对象,在构造器中创建了 DispSync 同步模型对象;
- 然后执行初始化 SurfaceFlinger 的逻辑:
- 注册监听,接收 HWC 的相关事件。
- 启动 APP 和 SF 的 EventThread 线程,用来管理基于 DispSync 创建的两个 DispSyncSource 延时源对象,分别是用于绘制(app–mEventThreadSource)和合成(SurfaceFlinger–mSfEventThreadSource)。启动了 EventThread 线程后,会一直阻塞在 waitForEventLocked 方法中(期间会根据需要设置监听器),直到接收到 Vsync 信号且至少有一个连接正在等待 Vsync 信号才会继续执行线程逻辑,即通知监听者;
- 通过 MessageQueue.setEventThread 方法创建了一个连接,并通过 Looper.addFd 方法监听 BitTube 数据。
- 创建 HWComposer 对象(通过 HAL 层的 HWComposer 硬件模块 或 软件模拟产生 Vsync 信号),现在的 Android 系统基本上都可以看成是通过硬件 HWComposer 产生 Vsync 信号,而不使用软件模拟,所以下面解析都只谈及硬件 HWComposer 的 Vsync 信号;
- 初始化非虚拟的显示屏;
- 启动开机动画服务;
- 最后执行 SurfaceFlinger.run 逻辑,该方法会在 SurfaceFlinger 主线程通过死循环执行 MessageQueue.waitMessage 方法等待消息的到来,其内部调用了 Looper.pollOnce 方法,该方法会从 Looper.addFd 方法监听的 BitTube 中读取数据,当有数据到来时执行对应的回调方法。
当硬件或软件模拟发出 Vsync 信号时:
- 回调 SF 相关方法,SF 调用 DispSync 同步模型的方法处理 Vsync 信号(统计和计算模型的偏移和周期),并根据返回值判断是否使能/关闭 HWC Vsync 信号的发出。
- DispSync 根据计算的偏移和周期计算下次 Vsync 信号发生时间,并通知监听者 Vsync 信号到达的事件,传递给 DispSyncSource 延时源,延时源通过 EventThread 来管理 Vsync 信号的收发。
- EventThread 调用连接 Connection 对象向 BitTube 发送数据,触发 addFd 函数中设置的回调方法,回调方法进而调用 SF.onMessageReceived 函数,然后进行图像的合成等工作。
另一方面,Choreographer 会通过上面创建的 APP 延时源 mEventThreadSource 对象及其对应的 EventThread 线程来监听同步模拟发出的 Vsync 信号,然后进行绘制(measure/layout/draw)操作。具体逻辑见 Android-Choreographer原理。
将 SurfaceFlinger 的工作流程总结如下图: