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

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影