Android SurfaceView 组件介绍,挖洞原理详解
文章目录
- 组件介绍
- 基本概念
- 关键特性
- 使用场景
- SurfaceHolder介绍
- 主要功能
- 使用示例
- SurfaceView 挖洞原理
- 工作机制
- 使用SurfaceView展示图片示例
- 创建一个自定义的 SurfaceView类
- 在 Activity 中使用 ImageSurfaceView
- 注意事项
- 效果展示
组件介绍
在 Android 开发中,SurfaceView
是一个非常特别的组件,它提供了一个专门的绘制表面,允许在主线程之外的线程上进行绘制操作。这使得 SurfaceView
非常适合需要高性能绘制和更新的场景,如视频播放和游戏渲染。这是因为它可以减少和避免 UI 线程的阻塞和延迟。
基本概念
SurfaceView
继承自 View
类,但与普通的 View
不同,它内部使用了一个独立的绘图表面(即 Surface
),这个 Surface
可以在主 UI 线程之外的线程上进行控制和绘制。这样做的好处是,即使绘制操作很复杂,也不会影响到 UI 线程的响应性和流畅性。
关键特性
-
独立的绘图表面:
SurfaceView
的Surface
是独立于应用窗口的其余部分,并且可以在单独的线程中进行更新和渲染。这意味着它不会受到其他视图层级更新的影响,从而提高了渲染效率。
-
线程安全:
- 由于
Surface
可以从任何线程进行访问和修改,因此SurfaceView
特别适用于后台线程渲染内容,如视频播放和动态图形。
- 由于
-
可见性管理:
SurfaceView
在屏幕上的可见性由 Android 的窗口管理器直接处理。当SurfaceView
变为不可见时,它的Surface
可能会被销毁,因此开发者需要在适当的生命周期回调中管理资源和绘制状态。
使用场景
- 视频播放:
SurfaceView
常用于视频播放应用,因为视频解码和渲染可以在单独的线程上进行,避免 UI 线程阻塞。 - 实时游戏渲染:游戏中的图形渲染需要高频率的更新和高性能,
SurfaceView
提供的独立绘图表面能够满足这些要求。 - 相机预览:相机应用中,
SurfaceView
可用于显示相机的实时预览流。
SurfaceHolder介绍
在Android开发中,SurfaceHolder
是一个接口,用于控制和监视Surface
对象的状态。Surface
是一个特殊的对象,它承载了一个可以绘制的矩形区域。这个区域可以由你的应用程序或其他应用程序来绘制。SurfaceView
类就是围绕Surface
提供一个可视组件的实现,而SurfaceHolder
提供了对这个Surface
的控制和管理。
主要功能
1. 访问和控制Surface
:
SurfaceHolder
允许开发者直接访问Surface
对象,这意味着你可以管理和绘制到Surface
上的图形内容。通过SurfaceHolder
,你可以获取Surface
的Canvas
,并在这个Canvas
上进行绘制操作。
2. 监听Surface
的状态变化:
SurfaceHolder
提供了一个回调机制,通过实现SurfaceHolder.Callback
接口,你可以监听Surface
的创建、改变和销毁事件。这些回调方法是:surfaceCreated(SurfaceHolder holder)
: 当Surface
第一次创建时调用,你应该在这个回调中开始绘制的操作。surfaceChanged(SurfaceHolder holder, int format, int width, int height)
: 当Surface
的格式或大小发生变化时调用。surfaceDestroyed(SurfaceHolder holder)
: 当Surface
即将被销毁时调用,你应该在这个回调中停止绘制操作,并进行清理。
3. 控制Surface
的格式和尺寸:
SurfaceHolder
允许开发者设置Surface
的尺寸、格式和类型。例如,你可以指定Surface
的分辨率和颜色深度。
使用示例
在使用SurfaceView
时,你通常会与SurfaceHolder
打交道。
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {private SurfaceHolder surfaceHolder;private DrawingThread drawingThread;public MySurfaceView(Context context) {super(context);init();}private void init() {surfaceHolder = getHolder();surfaceHolder.addCallback(this);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {drawingThread = new DrawingThread(holder);drawingThread.setRunning(true);drawingThread.start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {// Handle changes}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {boolean retry = true;drawingThread.setRunning(false);while (retry) {try {drawingThread.join();retry = false;} catch (InterruptedException e) {// handle interruption}}}private class DrawingThread extends Thread {private SurfaceHolder surfaceHolder;private boolean isRunning = false;public DrawingThread(SurfaceHolder holder) {this.surfaceHolder = holder;}public void setRunning(boolean isRunning) {this.isRunning = isRunning;}@Overridepublic void run() {while (isRunning) {Canvas canvas = null;try {canvas = surfaceHolder.lockCanvas();synchronized (surfaceHolder) {if (canvas != null) {// Perform drawing on the canvas}}} finally {if (canvas != null) {surfaceHolder.unlockCanvasAndPost(canvas);}}}}}
}
SurfaceHolder
是一个非常强大的工具,它为Surface
的管理提供了广泛的控制功能,同时使得在单独的线程中进行复杂的绘图操作成为可能,从而不影响应用程序的主UI线程的响应性。这在需要高性能绘图更新,如游戏或媒体播放器等应用中尤其重要。
SurfaceView 挖洞原理
在 Android 中,SurfaceView
是一个用于直接绘制图形的视图,通常用于游戏或视频播放等高性能需求的应用场景。SurfaceView
的挖洞原理主要涉及到 SurfaceView 的工作机制和它与窗口系统之间的交互。
工作机制
-
双缓冲机制:
SurfaceView
使用双缓冲机制,即前缓冲区和后缓冲区。前缓冲区显示在屏幕上,后缓冲区用于绘制新的内容。当后缓冲区绘制完成后,前后缓冲区交换,新的内容就会显示在屏幕上。这种机制可以减少屏幕闪烁和绘制延迟。 -
独立 Surface:
SurfaceView
创建了一个独立的 Surface,它与主 UI 线程分离,允许在另一个线程上进行绘制操作。这使得在 SurfaceView 上进行复杂的图形绘制时不会阻塞主 UI 线程,提高了绘制性能。 -
SurfaceHolder:
SurfaceView
通过SurfaceHolder
来管理其 Surface 的生命周期和绘制操作。SurfaceHolder
提供了一系列的回调方法,例如surfaceCreated
、surfaceChanged
和surfaceDestroyed
,用于监听 Surface 的创建、改变和销毁。
SurfaceView 挖洞的原理实际上是利用了 SurfaceView 绘制的特性,通过创建一个透明或空洞的区域,使得底层的内容可以透过 SurfaceView 显示出来。具体步骤如下:
-
设置透明背景:将
SurfaceView
的背景设置为透明,使得 SurfaceView 背景区域不绘制任何内容,从而形成“挖洞”效果。<SurfaceViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/transparent" />
-
绘制透明区域:在 SurfaceView 的 Canvas 上绘制一个透明区域,使得该区域不会绘制任何内容,从而露出底层视图。例如,可以使用
Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
方法清空指定区域。 -
层次叠加:利用视图的层次叠加关系,将
SurfaceView
放置在其他视图之上,并通过透明区域露出下层视图的内容。例如,可以在 SurfaceView 下方放置一个 ImageView 或其他自定义视图,通过透明区域显示底层视图的内容。
以下是一个简单的代码示例,演示了如何在 SurfaceView 中实现透明区域(“挖洞”):
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {private Paint mPaint;private Rect mRect;public MySurfaceView(Context context) {super(context);getHolder().addCallback(this);mPaint = new Paint();mPaint.setColor(Color.RED); // 设置绘制颜色mRect = new Rect(100, 100, 300, 300); // 定义透明区域}@Overridepublic void surfaceCreated(SurfaceHolder holder) {draw();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {}private void draw() {Canvas canvas = getHolder().lockCanvas();if (canvas != null) {canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // 清空整个画布canvas.drawRect(mRect, mPaint); // 绘制矩形区域getHolder().unlockCanvasAndPost(canvas);}}
}
这个示例创建了一个自定义的 SurfaceView
,并在其中绘制了一个矩形区域。通过 PorterDuff.Mode.CLEAR
模式,可以清空指定区域,使得该区域变得透明,从而实现“挖洞”效果。
使用SurfaceView展示图片示例
要在 Android 中使用 SurfaceView
在单独的线程中展示一张图片,我们可以通过创建一个自定义的 SurfaceView
类来实现。
示例代码:
创建一个自定义的 SurfaceView类
这个类将包含加载图片和在 SurfaceView
上绘制图片的逻辑:
public class ImageSurfaceView extends SurfaceView implements SurfaceHolder.Callback {private DrawThread drawThread;private Bitmap imageBitmap;public ImageSurfaceView(Context context,int imageResource) {super(context);this.imageBitmap = BitmapFactory.decodeResource(context.getResources(),imageResource);getHolder().addCallback(this);}@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {//开启新线程 绘制图片drawThread = new DrawThread(getHolder(), imageBitmap);drawThread.setRunning(true);drawThread.start();}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {//停止绘制线程boolean retry = true;drawThread.setRunning(false);while (retry) {try {drawThread.join();retry = false;} catch (InterruptedException e) {e.printStackTrace();}}}//新开一个线程用来绘制图片到Canvasprivate class DrawThread extends Thread{private SurfaceHolder surfaceHolder;private boolean isRunning = false;private Bitmap imageBitmap;public DrawThread(SurfaceHolder holder,Bitmap imageBitmap) {this.surfaceHolder = holder;this.imageBitmap = imageBitmap;}public void setRunning(boolean isRunning) {this.isRunning = isRunning;}@Overridepublic void run(){while(isRunning){Canvas canvas = null;//尝试获取canvas 绘制图片try {canvas = surfaceHolder.lockCanvas();if (canvas != null) {synchronized (surfaceHolder) {//绘制图片canvas.drawBitmap(imageBitmap, 0, 0, null);}}}finally {if (canvas != null) {surfaceHolder.unlockCanvasAndPost(canvas);}}}}}}
在 Activity 中使用 ImageSurfaceView
import android.app.Activity;
import android.os.Bundle;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 替换 R.drawable.your_image 为你的实际图片资源ImageSurfaceView imageSurfaceView = new ImageSurfaceView(this, R.drawable.your_image);setContentView(imageSurfaceView);}
}
注意事项
- 确保你的图片资源位于
res/drawable
文件夹中。 ImageSurfaceView
类中的DrawThread
在surfaceDestroyed
方法被调用时应该被正确地停止,以避免可能的内存泄露或崩溃。- 这个例子展示了在一个单独的线程中加载和绘制图片,以避免阻塞 UI 线程。
效果展示
注:这个dmeo仅仅展示了SurfaceView 新开线程展示图片效果,学习SurfaceView渲染流程,无法应用实际开发,如果用于实际开发,如开发自定义控件,还需进一步完善自定义的类
相关文章:

Android SurfaceView 组件介绍,挖洞原理详解
文章目录 组件介绍基本概念关键特性使用场景 SurfaceHolder介绍主要功能使用示例 SurfaceView 挖洞原理工作机制 使用SurfaceView展示图片示例创建一个自定义的 SurfaceView类在 Activity 中使用 ImageSurfaceView注意事项效果展示 组件介绍 在 Android 开发中,Sur…...
day2加餐 Go 接口型函数的使用场景
文章目录 问题价值使用场景其他语言类似特性 问题 在 动手写分布式缓存 - GeeCache day2 单机并发缓存 这篇文章中,有一个接口型函数的实现: // A Getter loads data for a key. type Getter interface {Get(key string) ([]byte, error) }// A Getter…...

摄像头 RN6752v1 视频采集卡
摄像头 AHD倒车摄像头比较好,AHD英文全名Analog High Definition,即模拟高清,拥有比较好的分辨率与画面质感。 RN6752v1 GQW AKKY2 usb 采集卡 FHD(1080p)、HD(720p)和D1(480i&am…...

记录vivado自带IP iBert眼图近端回环
记录利用vivado自带IP核工具测试信号质量 ibert是测试眼图的工具,在使用的时候并不用改太多的内容,只需要注意参考时钟及所需要的引脚即可。由于条件的限制,并没有使用光纤和电缆进行连接进行外部回环,仅使用内部回环做测试&…...

js | Core
http://dmitrysoshnikov.com/ecmascript/javascript-the-core/ Object 是什么? 属性[[prototype]]对象。 例如,下面的,son是对象,foo不是对象。打印出来的son,能看到有一个prototype 对象。 prototype vs _proto_ v…...
Log4J reminder
Java JNDI and Log injection https://docs.oracle.com/javase/jndi/tutorial/ See also https://telegra.ph/Log4J-Vulnerability-Explained-07-21...

Unity XR Interaction Toolkit(VR、AR交互工具包)记录安装到开发的流程,以及遇到的常见问题(一)!
提示:文章有错误的地方,还望诸位大神不吝指教! 文章目录 前言一、XR Interaction Toolkit是什么?二、跨平台交互三、 AR 功能四、XR Interaction Toolkit的特点五、XR Interaction Toolkit 示例总结 前言 随着VR行业的发展&#…...

MongoDB文档整理
过往mongodb文档: https://blog.csdn.net/qq_46921028/article/details/123361633https://blog.csdn.net/qq_46921028/article/details/131136935https://blog.csdn.net/qq_46921028/article/details/139247847 1. MongoDB前瞻 1、MongoDB概述: MongoDB是…...

【AI学习】关于Scaling Law的相关学习
一、苦涩的教训 首先,学习一段重要话语: The biggest lesson that can be read from 70 years of AI research is that general methods that leverage computation are ultimately the most effective, and by a large margin. 从70年的人工智能研究中…...
学习小记-Kafka相较于其他MQ有啥优势?
Kafka 相比于 RocketMQ 有以下几个优势: 1. 高吞吐量和低延迟: Kafka 以其出色的 I/O 性能和分布式架构设计,能够实现极高的吞吐量,每秒数百万的消息处理能力,适合大规模数据流处理。同时,Kafka 设计为…...

技能 | postman接口测试工具安装及使用
哈喽小伙伴们大家好!今天来给大家分享一款轻量级,高效好用的接口测试工具-postman. Postman是一个流行的API开发工具,主要用于测试、开发和文档化API。以下是关于Postman的介绍及其主要使用场景: Postman介绍: 1. 功能丰富的API客户端&#…...

移动UI:任务中心的作用,该如何设计更合理?
任务中心是移动应用中用于展示和管理用户待办任务、提醒事项、用户福利、打卡签到等内容的功能模块。合理设计任务中心可以提升用户体验和工作效率。 以下是一些设计任务中心的合理建议: 1. 易于查看和管理: 任务中心的设计应该使用户能够快速、直观地…...

pytorch学习(十)优化函数
优化函数主要有,SGD, Adam,RMSProp这三种,并且有lr学习率,momentum动量,betas等参数需要设置。 通过这篇文章,可以学到pytorch中的优化函数的使用。 1.代码 代码参考《python深度学习-基于pytorch》&…...
Ubuntu22.04:安装Samba
1.安装Samba服务 $ sudo apt install samba samba-common 2.创建共享目录 $ mkdir /home/xxx/samba $ chmod 777 /home/xxx/samba 3.将用户加入到Samba服务中 $ sudo smbpasswd -a xxx 设置用户xxx访问Samba的密码 4.配置Samba服务 $ sudo vi /etc/samba/smb.conf 在最后加入 …...
Powershell 使用介绍
0 Preface/Foreword 0.1 参考文档 Starting Windows PowerShell - PowerShell | Microsoft Learn 1 Powershell 介绍 2 命令介绍 2.1 新建文件夹 New-Item -Path C:\GitLab-Runner -ItemType Directory 2.2 切换路径 cd C:\GitLab-Runner 2.3 下载文件 Invoke-WebRequ…...

【Langchain大语言模型开发教程】记忆
🔗 LangChain for LLM Application Development - DeepLearning.AI 学习目标 1、Langchain的历史记忆 ConversationBufferMemory 2、基于窗口限制的临时记忆 ConversationBufferWindowMemory 3、基于Token数量的临时记忆 ConversationTokenBufferMemory 4、基于历史…...

最新Qt6的下载与成功安装详细介绍
引言 Qt6 是一款强大的跨平台应用程序开发框架,支持多种编程语言,最常用的是C。Qt6带来了许多改进和新功能,包括对C17的支持、增强的QML和UI技术、新的图形架构,以及构建系统方面的革新。本文将指导你如何在Windows平台上下载和安…...

LeetCode 热题 HOT 100 (001/100)【宇宙最简单版】
【链表】 No. 0160 相交链表 【简单】👉力扣对应题目指路 希望对你有帮助呀!!💜💜 如有更好理解的思路,欢迎大家留言补充 ~ 一起加油叭 💦 欢迎关注、订阅专栏 【力扣详解】谢谢你的支持&#x…...

Ubantu 使用 docker 配置 + 远程部署 + 远程开发
大家好我是苏麟 , Ubantu 一些配置 . 视频 : 服务器很贵?搞台虚拟机玩玩!保姆级 Linux 远程开发教程_哔哩哔哩_bilibili Docker安装及配置 安装命令 : sudo apt install docker.io 查看版本号 : docker -v 查看虚拟机地址命令 : ifconfig 虚拟机地址 或…...

应用层自定义协议与序列化
个人主页:Lei宝啊 愿所有美好如期而遇 协议 简单来说,就是通信双方约定好的结构化的数据。 序列化与反序列化 我们通过一个问题引入这个概念,假如我们要实现一个网络版的计算器,那么现在有两种方案,第一种&#x…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...

macOS 终端智能代理检测
🧠 终端智能代理检测:自动判断是否需要设置代理访问 GitHub 在开发中,使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新,例如: fatal: unable to access https://github.com/ohmyzsh/oh…...