当前位置: 首页 > news >正文

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 树的单线程绘制流程,直接对接系统的图形合成层。

http://www.cnnetsun.cn/news/3427232.html

相关文章:

  • 华硕主板传感器识别限制深度解析:FanControl多维度技术解决方案实战指南
  • AI Agent不是万能胶:这6类业务场景必须立即停用,否则ROI为负(附自查清单)
  • 仅限前200名开发者获取:Cursor全栈开发实战速查图谱(含17个高频场景Prompt库)
  • 猫抓cat-catch:浏览器资源嗅探扩展的终极完整指南
  • Ruduino核心功能解析:寄存器操作与引脚控制的简单实现
  • 免费iOS激活锁绕过工具applera1n:iPhone 6s至iPhone X设备终极解决方案
  • 指纹浏览器技术演进分析:从Multilogin到新一代多账号管理平台的架构对比
  • 四足机器人开发的终极解决方案:Unitree GO2 ROS2 SDK深度探索
  • 如何利用Knowledge构建团队协作知识库:完整用户指南
  • 3步免费搭建专属游戏下载中心:FitGirl Repack Launcher完整指南
  • Python开发小说阅读器:从入门到实践
  • Darknet/YOLO命令行工具大全:从图像检测到模型评估
  • 信息学奥赛一本通 1161:转进制——从递归到迭代的进制转换实战解析
  • 从零代码转向Python量化前要补齐的四类能力
  • m4s-converter:如何将B站缓存视频从数字枷锁中解放出来?
  • 【2024最严苛Claude推理测评】:覆盖23个专业领域、412道链式推理题,仅3款配置通过全量验证
  • C++安全字符串分割:strtok_s详解与多线程实战
  • 如何高效掌握 LeetCode 算法?Algorithms-Leetcode-Javascript 项目实战指南
  • Maven实战:从零构建你的第一个Java项目
  • 如何高效批量下载抖音内容:一个免费开源工具的完整指南
  • FitGirl Repack Launcher完整指南:打造高效游戏下载管理平台
  • 企业级Codex AI落地白皮书(含金融/医疗/嵌入式三大领域合规校验清单)
  • Windows批处理脚本入门与高效自动化实践
  • Bebas Neue字体完全指南:免费开源字体如何改变你的设计游戏
  • openEuler服务能力参考:诊断分析服务的7步方法论
  • ft_wl_fwk输出管理:多显示器支持和分辨率配置详解
  • Cursor + TypeScript + Prisma全链路开发闭环(含CI/CD自动部署全流程)
  • Reloaded-II:终极免费通用游戏模组加载器,三步打造你的专属游戏世界
  • Nemotron-3-Embed-1B-BF16技术内幕:Ministral-3模型剪枝与知识蒸馏全过程
  • ctdeployer核心功能全解析:从批量安装到安全配置的完整流程