news 2026/7/16 14:02:17

Android随笔-为什么SurfaceView/TextureView可以在子线程绘制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android随笔-为什么SurfaceView/TextureView可以在子线程绘制

一、核心结论

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);}

为什么可以子线程?

  1. Surface 是独立的缓冲区:不经过 View 树的draw()流程
  2. 直接对接 SurfaceFlingerunlockCanvasAndPost()直接把缓冲区交给系统合成
  3. 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 对比

特性SurfaceViewTextureView
渲染层独立 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 系统合成
子线程绘制的关键 APISurfaceHolder.lockCanvas()/unlockCanvasAndPost()
子线程 OpenGL 绘制的关键EGL 上下文 +eglSwapBuffers()
它们破了 Android 的 UI 单线程规矩吗没有,它们只是绕过了 View 系统的绘制流程
普通 View 为什么不行普通 View 的 Canvas 由 Choreographer 在主线程分配,且 View 树状态非线程安全

一句话:SurfaceView 和 TextureView 能在子线程绘制,是因为它们拥有独立的渲染表面,不依赖 View 树的单线程绘制流程,直接对接系统的图形合成层。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/16 13:59:40

猫抓cat-catch:浏览器资源嗅探扩展的终极完整指南

猫抓cat-catch:浏览器资源嗅探扩展的终极完整指南 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 猫抓cat-catch是一款功能强大的浏览器…

作者头像 李华
网站建设 2026/7/16 13:59:07

Ruduino核心功能解析:寄存器操作与引脚控制的简单实现

Ruduino核心功能解析:寄存器操作与引脚控制的简单实现 【免费下载链接】ruduino Reusable components for the Arduino Uno. 项目地址: https://gitcode.com/gh_mirrors/ru/ruduino Ruduino是一个为Arduino Uno设计的Rust语言硬件抽象库,它提供了…

作者头像 李华