Android开发系列:高性能视图组件Surfaceview

2024-06-16 22:12

本文主要是介绍Android开发系列:高性能视图组件Surfaceview,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、Surfaceview概述

在Android应用开发领域,面对视频播放、游戏构建及相机实时预览等高性能需求场景,直接操控图像数据并即时展示于屏幕成为必要条件。传统View组件在此类情境下显现局限性:

  • 性能瓶颈:传统View的绘制任务由UI主线程承担,如果绘制操作过于复杂或需要频繁刷新,就可能导致主线程阻塞,进而影响界面的响应速度和用户交互体验。
  • 视觉瑕疵:传统View组件缺乏双缓冲技术的支持,View直接屏幕绘制易引发画面闪烁及图像撕裂。
  • 效果局限:传统View组件基于视图层次结构,每个View都被视为一个矩形区域,这使得实现不规则形状、透明度变化等复杂视觉效果变得相对困难。

鉴于上述挑战,Android引入了SurfaceView作为解决方案。这一特殊视图组件具备独立的Surface层(图形缓冲区),实现内容的离线绘制,特点如下:

  • 独立渲染SurfaceView的Surface层独立于主UI线程,确保复杂绘图操作不干扰UI响应,提升应用流畅性。
  • 双缓冲机制:通过双缓冲机制,有效缓解画面闪烁与撕裂现象,提升视觉呈现质量。
  • 透明度支持:透明的Surface设计使其能无缝融入视图层级,支持与其他View叠加或裁剪,解锁复杂视觉效果的实现潜能。
  • 支持OpenGL ES:SurfaceView支持OpenGL ES库,这意味着它可以实现2D和3D图形效果,对于游戏、视频等性能要求较高的应用非常有用。

总之,SurfaceView凭借其独特的架构优势,优化了高性能应用场景下的渲染效率与用户体验,是处理视频播放、游戏动画及实时预览等需求的理想选择,成功绕过了常规View组件的诸多障碍。

二、工作原理

核心类:

  • Surface:Surface创建了一个独立的绘图表面。这个独立的Surface允许SurfaceView在一个单独的线程中进行UI绘制,从而避免占用主线程资源,提高应用性能。
  • SurfaceHolder:SurfaceHolder负责管理Surface 的生命周期,并提供了一系列回调方法,以便开发者获取 Surface 的状态变化。
  • SurfaceView:SurfaceView 是一个View组件,它内部封装了一个SurfaceHolder,以便将 Surface 呈现到屏幕上。

SurfaceView、Surface、和SurfaceHolder之间的关系可以类比为MVC架构。其中,Model对应数据模型,对应Surface;View对应视图,对应SurfaceView;Controller对应控制器,对应SurfaceHolder。

在Android应用程序的Activity布局中,多种View组件相互嵌套,形成了一个层次分明的View hierarchy(视图层级结构)。这个结构的最顶端是DecorView,它是整个View树与Window Manager Service(WMS)之间的桥梁,WMS仅直接与这个根视图交互,并为之分配一个WindowState对象来管理视图的显示属性。同时,在SurfaceFlinger(SF)系统框架下,DecorView同样获得一个Layer,负责在屏幕上的最终合成与显示。

不同于普通View,SurfaceView内嵌了一个独立的Surface,这是一个用于直接绘图的缓冲区。这个Surface在WMS中同样注册了一个WindowState,意味着它能独立参与窗口管理,享有与DecorView类似的管理机制。并且,在SF中,SurfaceView的Surface也会被赋予一个单独的Layer,这样的设计允许SurfaceView在不干扰UI线程的情况下,由专有线程高效地进行图形更新,非常适合处理如视频播放或复杂动画等高负载、高频率刷新的场景,从而极大提升了图形渲染的效率和应用的流畅度。

在这里插入图片描述
SurfaceView像是在窗口上挖一个洞,它就是显示在这个洞里,传统的View是显示在窗口上。实际上,SurfaceView的Layer在Z轴上的位置小于其宿主Activity窗口的Layer,因此它默认是被遮挡的。但SurfaceView提供了一个透明区域,使得只有在这个区域内的内容才对用户可见。

详细的原理代码,可以移步:https://www.jianshu.com/p/5e5ae2f524ce

三、应用实践

1. 创建SurfaceView
在布局文件中添加SurfaceView,或者在代码中动态创建。
2. 获取SurfaceHolder,添加和实现Callback
创建一个类实现SurfaceHolder.Callback接口,以便监听SurfaceView的创建surfaceCreated、销毁surfaceDestroyed和状态改变surfaceChanged。
在你的SurfaceView实现类中,获取到SurfaceHolder实例,并使用addCallback方法添加之前创建的Callback实例。

3. 绘图
在绘图线程中,通过SurfaceHolder获取Canvas对象,并在其上绘制图形。

Canvas canvas= mSurfaceHolder.lockCanvas();

创建一个新的线程,在这个线程里不断地执行绘图逻辑。通常会在这个线程中锁定Canvas,然后进行绘制,最后解锁并提交绘制结果。

if (canvas != null){mSurfaceHolder.unlockCanvasAndPost(canvas);
}

4. 处理Surface变化
在surfaceChanged回调中处理Surface尺寸或格式的变化。
5. 清理资源
在surfaceDestroyed回调中,停止绘图线程并释放所有相关资源。

示例

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {private SurfaceHolder holder;private DrawThread drawThread;public MySurfaceView(Context context) {super(context);init();}public MySurfaceView(Context context, AttributeSet attrs) {super(context, attrs);init();}public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {holder = getHolder();holder.addCallback(this);// 设置SurfaceView的格式,使其支持透明度holder.setFormat(PixelFormat.TRANSPARENT);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {drawThread = new DrawThread(holder);drawThread.start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {// 重新配置绘图环境}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {boolean retry = true;drawThread.running = false;while (retry) {try {drawThread.join();retry = false;} catch (InterruptedException e) {// nothing to do}}}class DrawThread extends Thread {private SurfaceHolder mSurfaceHolder;volatile boolean running = true;public DrawThread(SurfaceHolder surfaceHolder) {mSurfaceHolder = surfaceHolder;}@Overridepublic void run() {Canvas canvas;while (running) {canvas = null;try {canvas = mSurfaceHolder.lockCanvas(null);synchronized (mSurfaceHolder) {// 在这里执行具体的绘图操作}} finally {if (canvas != null) {mSurfaceHolder.unlockCanvasAndPost(canvas);}}}}}
}

四、注意事项

  • 线程同步问题:因为SurfaceView的绘图通常在单独的线程中进行,所以确保线程间的同步至关重要。不当的同步可能导致绘制错误、图像撕裂或应用崩溃。使用锁机制(如synchronized关键字)或条件变量来协调资源访问。

  • 资源泄漏:忘记在surfaceDestroyed中正确清理资源,特别是线程和绘图相关的资源,会导致内存泄漏。确保在不再需要时及时停止和回收所有资源。

  • 绘制效率:频繁的锁住和解锁Canvas会降低性能。尽量减少这些操作的次数,比如通过批量绘制或者优化绘图逻辑来提高效率。

  • 屏幕旋转与配置变更:当设备旋转或系统配置变更时,SurfaceView可能会被销毁和重建。需要在onSaveInstanceState中保存必要的状态,并在onCreate或onRestoreInstanceState中恢复,确保平滑过渡。

  • 触摸事件处理:默认情况下,触摸事件可能不会传递给SurfaceView。需要重写onTouchEvent方法,并可能需要调整视图的setZOrderOnTop属性,确保能够正确接收并处理触摸事件。

  • 后台绘制与电池消耗:即使应用退至后台,如果绘图线程没有正确暂停,也可能持续消耗CPU和电池资源。确保在onPause和onResume中管理绘图线程的状态。

  • Surface生命周期管理:正确处理SurfaceHolder.Callback中的生命周期方法,尤其是surfaceCreated、surfaceChanged和surfaceDestroyed。例如,避免在surfaceDestroyed后继续尝试访问Surface。surfaceView可见时才被创建,隐藏时就被销毁。

  • 硬件加速与兼容性:硬件加速可能与SurfaceView的某些特性不兼容,导致渲染问题。理解何时开启或关闭硬件加速,并测试不同设备和Android版本的兼容性。

  • 内存管理:大图或过多的Bitmap操作可能导致OutOfMemoryError。合理使用Bitmap的采样策略、及时回收Bitmap对象,以及考虑使用更高效的图像加载库(如Glide或Picasso)。

  • 与Activity/Fragment生命周期的协调:确保SurfaceView的生命周期与包含它的Activity或Fragment保持一致,避免在Activity或Fragment销毁后继续运行,引发内存泄漏或异常。

这篇关于Android开发系列:高性能视图组件Surfaceview的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1067687

相关文章

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

Android实现悬浮按钮功能

《Android实现悬浮按钮功能》在很多场景中,我们希望在应用或系统任意界面上都能看到一个小的“悬浮按钮”(FloatingButton),用来快速启动工具、展示未读信息或快捷操作,所以本文给大家介绍... 目录一、项目概述二、相关技术知识三、实现思路四、整合代码4.1 Java 代码(MainActivi

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案

《Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案》:本文主要介绍Vue3组件中getCurrentInstance()获取App实例,但是返回nu... 目录vue3组件中getCurrentInstajavascriptnce()获取App实例,但是返回n

如何解决idea的Module:‘:app‘platform‘android-32‘not found.问题

《如何解决idea的Module:‘:app‘platform‘android-32‘notfound.问题》:本文主要介绍如何解决idea的Module:‘:app‘platform‘andr... 目录idea的Module:‘:app‘pwww.chinasem.cnlatform‘android-32

使用Python开发一个带EPUB转换功能的Markdown编辑器

《使用Python开发一个带EPUB转换功能的Markdown编辑器》Markdown因其简单易用和强大的格式支持,成为了写作者、开发者及内容创作者的首选格式,本文将通过Python开发一个Markd... 目录应用概览代码结构与核心组件1. 初始化与布局 (__init__)2. 工具栏 (setup_t

Android实现打开本地pdf文件的两种方式

《Android实现打开本地pdf文件的两种方式》在现代应用中,PDF格式因其跨平台、稳定性好、展示内容一致等特点,在Android平台上,如何高效地打开本地PDF文件,不仅关系到用户体验,也直接影响... 目录一、项目概述二、相关知识2.1 PDF文件基本概述2.2 android 文件访问与存储权限2.

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

SpringQuartz定时任务核心组件JobDetail与Trigger配置

《SpringQuartz定时任务核心组件JobDetail与Trigger配置》Spring框架与Quartz调度器的集成提供了强大而灵活的定时任务解决方案,本文主要介绍了SpringQuartz定... 目录引言一、Spring Quartz基础架构1.1 核心组件概述1.2 Spring集成优势二、J

Android Studio 配置国内镜像源的实现步骤

《AndroidStudio配置国内镜像源的实现步骤》本文主要介绍了AndroidStudio配置国内镜像源的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、修改 hosts,解决 SDK 下载失败的问题二、修改 gradle 地址,解决 gradle