一、核心结论
SurfaceView 和 TextureView 能在子线程绘制,不是因为它们破了 Android 的 UI 单线程规矩,而是它们根本不走 View 系统的绘制流程。
二、普通 View 的绘制流程(主线程)
Activity → PhoneWindow → DecorView → ViewRootImpl ↓ Choreographer(主线程) ↓ measure → layout → draw ↓ Canvas(由 SurfaceFlinger 提供) ↓ 同步到屏幕普通 View 的onDraw(Canvas)是在主线程被 Choreographer 回调触发的,所以必须在主线程执行。
三、SurfaceView 的子线程绘制原理
核心:双 Surface 机制
Activity 的窗口(PhoneWindow): ┌─────────────────────────────┐ │ DecorView │ │ ┌───────────────────────┐ │ │ │ 普通 View 区域 │ │ ← 主线程绘制 │ │ (TextView, Button) │ │ │ ├───────────────────────┤ │ │ │ SurfaceView 占位 │ │ ← 只是一个"透明洞" │ │ (实际不在这里画) │ │ │ └───────────────────────┘ │ └─────────────────────────────┘ ↓ 独立的 Surface(在窗口后面/独立层) ┌─────────────────────────────┐ │ 子线程直接绘制到这里 │ ← 子线程 Canvas │ (MediaPlayer, Camera, 游戏) │ └─────────────────────────────┘ ↓ SurfaceFlinger 合成所有层 → 屏幕SurfaceView 内部有两个关键对象:
| 对象 | 作用 |
|---|---|
| Surface | 独立的图形缓冲区,与 SurfaceFlinger 直接交互 |
| SurfaceHolder | 管理 Surface 的生命周期,提供lockCanvas() |
代码示例
publicclassMySurfaceViewextendsSurfaceViewimplementsSurfaceHolder.Callback{privateSurfaceHolderholder;privateRenderThreadrenderThread;publicMySurfaceView(Contextcontext){super(context);holder=getHolder();holder.addCallback(this);}@OverridepublicvoidsurfaceCreated(SurfaceHolderholder){// Surface 已创建,启动子线程渲染renderThread=newRenderThread(holder);renderThread.start();}@OverridepublicvoidsurfaceDestroyed(SurfaceHolderholder){renderThread.stopRendering();// 停止渲染}// 子线程渲染privateclassRenderThreadextendsThread{privateSurfaceHolderholder;privatebooleanrunning=true;RenderThread(SurfaceHolderholder){this.holder=holder;}@Overridepublicvoidrun(){while(running){Canvascanvas=null;try{// 锁住 Surface 的 Canvas,获取绘制权canvas=holder.lockCanvas();// 在子线程中直接绘制!canvas.drawColor(Color.BLACK);canvas.drawCircle(100,100,50,paint);}finally{if(canvas!=null){// 提交绘制内容,释放 Canvasholder.unlockCanvasAndPost(canvas);}}}}voidstopRendering(){running=false;}}}关键方法
// SurfaceHolder.javapublicinterfaceSurfaceHolder{// 获取 Canvas,锁定 Surface 进行绘制CanvaslockCanvas();// 提交绘制内容,SurfaceFlinger 会将其合成到屏幕voidunlockCanvasAndPost(Canvascanvas);}为什么可以子线程?
- Surface 是独立的缓冲区:不经过 View 树的
draw()流程 - 直接对接 SurfaceFlinger:
unlockCanvasAndPost()直接把缓冲区交给系统合成 - View 树只负责"挖洞":SurfaceView 在 View 树中只是一个占位,实际内容在另一个层
四、TextureView 的子线程绘制原理
核心:SurfaceTexture + OpenGL
TextureView 比 SurfaceView 更灵活,它把内容渲染到一个OpenGL ES 纹理上,然后这个纹理被 View 系统正常合成。
TextureView ↓ SurfaceTexture(OpenGL ES 纹理) ↓ 子线程通过 OpenGL 渲染到纹理 ↓ View 系统把纹理作为普通 View 内容合成 ↓ 屏幕代码示例
publicclassMyTextureViewextendsTextureViewimplementsTextureView.SurfaceTextureListener{privateRenderThreadrenderThread;publicMyTextureView(Contextcontext){super(context);setSurfaceTextureListener(this);}@OverridepublicvoidonSurfaceTextureAvailable(SurfaceTexturesurface,intwidth,intheight){// SurfaceTexture 可用,启动子线程渲染renderThread=newRenderThread(surface);renderThread.start();}@OverridepublicbooleanonSurfaceTextureDestroyed(SurfaceTexturesurface){renderThread.stopRendering();returntrue;}// 子线程用 OpenGL 渲染privateclassRenderThreadextendsThread{privateSurfaceTexturesurfaceTexture;privateEGLSurfaceeglSurface;privatebooleanrunning=true;RenderThread(SurfaceTexturesurfaceTexture){this.surfaceTexture=surfaceTexture;}@Overridepublicvoidrun(){// 初始化 EGL 环境initEGL();while(running){// 在子线程中用 OpenGL 绘制GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);GLES20.glClearColor(1.0f,0.0f,0.0f,1.0f);// 交换缓冲区,内容更新到 SurfaceTextureEGL14.eglSwapBuffers(eglDisplay,eglSurface);surfaceTexture.updateTexImage();// 更新纹理}}voidstopRendering(){running=false;}}}五、SurfaceView vs TextureView 对比
| 特性 | SurfaceView | TextureView |
|---|---|---|
| 渲染层 | 独立 Surface(窗口后面) | OpenGL 纹理(View 树内部) |
| 子线程绘制 | ✅ 通过lockCanvas() | ✅ 通过 OpenGL EGL |
| 可以透明/半透明 | ❌ 不支持(独立层) | ✅ 支持(普通 View 合成) |
| 可以旋转/缩放 | ❌ 不支持 | ✅ 支持(普通 View 变换) |
| 可以截图 | ❌ 不支持 | ✅ 支持 |
| 性能 | 高(直接硬件层合成) | 较低(需要 GPU 纹理拷贝) |
| 适用场景 | 游戏、视频播放、相机预览 | 需要变换效果的视频、流媒体 |
| Z-order 问题 | 永远在普通 View 下面 | 和普通 View 一样 |
六、底层原理:SurfaceFlinger 的合成
┌─────────────────────────────────────────┐ │ SurfaceFlinger(系统服务) │ │ │ │ ┌─────────┐ ┌─────────┐ ┌────────┐ │ │ │ App UI │ │SurfaceV │ │ Status │ │ │ │ Layer │ │ Layer │ │ Bar │ │ │ │ (主线程) │ │(子线程) │ │ │ │ │ └────┬────┘ └────┬────┘ └───┬────┘ │ │ └─────────────┴─────────────┘ │ │ ↓ 合成 │ │ 屏幕显示 │ └─────────────────────────────────────────┘SurfaceFlinger 是 Android 系统的合成器,它收集所有应用的图形层(Layer),按 Z-order 合成到屏幕。
- 普通 View:在一个 Layer 里,由主线程绘制
- SurfaceView:独立的 Layer,由子线程直接绘制,SurfaceFlinger 单独合成
- TextureView:在一个 Layer 里,但内容是 OpenGL 纹理,子线程更新纹理后 View 系统再合成
七、总结
| 问题 | 答案 |
|---|---|
| 为什么 SurfaceView 可以子线程绘制 | 它有独立的 Surface,不经过 View 树的 draw() 流程 |
| 为什么 TextureView 可以子线程绘制 | 它用 OpenGL 纹理,子线程更新纹理后 View 系统合成 |
| 子线程绘制的关键 API | SurfaceHolder.lockCanvas()/unlockCanvasAndPost() |
| 子线程 OpenGL 绘制的关键 | EGL 上下文 +eglSwapBuffers() |
| 它们破了 Android 的 UI 单线程规矩吗 | 没有,它们只是绕过了 View 系统的绘制流程 |
| 普通 View 为什么不行 | 普通 View 的 Canvas 由 Choreographer 在主线程分配,且 View 树状态非线程安全 |
一句话:SurfaceView 和 TextureView 能在子线程绘制,是因为它们拥有独立的渲染表面,不依赖 View 树的单线程绘制流程,直接对接系统的图形合成层。