-
概述
-
TextureView优缺点
越来越多的应用需要使用自己的绘制引擎进行复杂内容的绘制,比如需要使用 GL 绘制 3D 的内容,或者绘制复杂的文档,图表时不希望阻塞 UI 线程,或者部分内容是通过类似 Flutter 这样的第三方 UI Toolkit 进行绘制。通常这部分内容会通过 SurfaceView 或者 TextureView 呈现在 UI 界面上。
一般来说 SurfaceView 能够提供更好的性能,但是因为 SurfaceView 本身的输出不是通过 Android 的 UI Renderer(HWUI),而是直接走系统的窗口合成器 SurfaceFlinger,所以无法实现对普通 View 的完全兼容。包括不支持 transform 动画,不支持半透明混合,移动,大小改变,隐藏/显示等时机会出现各种瑕疵等等,总的来说 SurfaceView 只适用于有限的场景。
TextureView 正是为了解决 SurfaceView 这些的问题而诞生,在使用上基本可以无缝替换 SurfaceView,并且因为 TextureView 跟普通 View 一样是通过 UI Renderer 绘制到当前 Activity 的窗口上,所以它跟普通 View 基本上是完全兼容的,不存在 SurfaceView 的种种问题。但同时正是因为 TextureView 需要通过 UI Renderer 输出,也导致了新的问题的出现。除了性能比较 SurfaceView 会有明显下降外(低端机,高 GPU 负荷场景可能存在 15% 左右的帧率下降),另外因为需要在三个线程之间进行写读同步(包括 CPU 和 GPU 的同步),当同步失调的时候,比较容易出现掉帧或者吞帧导致的卡顿和抖动现象。
-
绘制流程
-
TextureView 绘制和输出
TextureView 的绘制和输出通常会涉及三个线程:
- 我们自己创建的用于绘制 TextureView 的线程(当然实际上是绘制 TextureView 创建的 SurfaceTexture,通过 Surface 接口),在这里我们称之为 TextureView Render 线程(实际的名字取决于应用的绘制引擎);
- 系统创建的用于操作 View 的 UI 线程,也是应用的主线程;
- 系统创建的用于绘制所有 View 的内容到当前 Activity 窗口的 Android Render 线程;
一般情况下,TextureView Render 和 UI 线程都是由 VSync 信号驱动的(Choreographer 的回调),而 Android Render 线程是由 UI 线程驱动的。
-
SurfaceTexture( TextureView Render 线程)
TextureView 本质上是 SurfaceTexture 的 View 封装,而 SurfaceTexture 本质上是一个 Buffer Queue。当我们使用 GL 绘制 SurfaceTexture 时(SurfaceTexture 包装成 Surface 作为当前上下文的 Window Surface):
- 新的一帧的第一个 GL Draw Call 会触发一个 Dequeue Buffer 的操作;
- 后续的 GL Draw Calls 都绘制到这个 Buffer 上;
- 调用 eglSwapBuffers 会触发一个 Queue Buffer 的操作;
- Queue Buffer 会导致 SurfaceTexture 发送一个 OnFrameAvailable 的消息回 UI 线程;
-
View 的 UI 线程
先看下 TextureView 输出的流程:
//文件:TextureView.java
private final SurfaceTexture.OnFrameAvailableListener mUpdateListener =
new SurfaceTexture.OnFrameAvailableListener() {
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
updateLayer();
invalidate();
}
};
private void updateLayer() {
synchronized (mLock) {
mUpdateLayer = true;
}
}
@Override
public final void draw(Canvas canvas) {
...
if (canvas.isHardwareAccelerated()) {
DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
TextureLayer layer = getTextureLayer();
if (layer != null) {
applyUpdate();
applyTransformMatrix();
mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date
displayListCanvas.drawTextureLayer(layer);
}
}
}
- 如上面的 TextureView 代码所示,TextureView 通过回调在 UI 线程收到 SurfaceTexture 的 OnFrameAvailable 消息后,它会先 UpdateLayer 然后再 Invalidate 自己;
- UpdateLayer 只是设置一个 mUpdateLayer 的标记,Invalidate 会触发 UI 绘制下一帧;
- UI 线程在绘制新的一帧的过程,图中的Draw(CPU),实际上就是调用被 Invalidated 的 View 的 draw 方法;
- TextureView 的 draw 主要做两件事情,第一是 applyUpdate,applyUpdate 实际上是生成一个 UpdateSurfaceTexture 的任务等待 Android Render 线程执行,第二是生成一个 DrawTextureLayer 的指令到自己的 DisplayList,这里的 TextureLayer 实际上又是对 SurfaceTexture 的封装;
- 当 UI 线程的 Draw(CPU) 完成后,意味着更新完所有需要更新的 View 的 DisplayList,此时它会请求 Android Render 线程开始绘制这些 DisplayList;
-
Android Render 线程

文章详细阐述了TextureView的诞生背景,作为SurfaceView的补充,解决了SurfaceView的一些兼容性问题。TextureView的绘制涉及到三个线程,包括自定义的绘制线程、UI线程和AndroidRender线程,其输出流程增加了额外的GPU开销和VSync周期的延迟。尽管性能稍逊于SurfaceView,但更长的渲染流水线意味着更高的吞吐量和宽容度,适合一般应用。文章还介绍了TextureView的创建、更新和绘制流程,以及如何通过OnFrameAvailableListener监听帧更新。
&spm=1001.2101.3001.5002&articleId=130421959&d=1&t=3&u=daceda8dffa04d098b0095abacc60832)
2134

被折叠的 条评论
为什么被折叠?



