Android 匿名共享内存的使用

注:本文内容转载自如下文章:Android 匿名共享内存的使用

Android View 的绘制是如何把数据传递给 SurfaceFlinger 的呢? 跨进程通信时,数据量大于1MB要怎么传递呢?用 匿名共享内存(Ashmem) 是个不错的选择,它不仅可以减少内存复制的次数,还没有内存大小的限制。这篇文章介绍在 Java 层如何使用匿名共享内存在进程间传递数据。

1. 简述

Android匿名共享内存(Ashmem) 基于 Linux 的共享内存,都是在临时文件系统(tmpfs)上创建虚拟文件,再映射到不同的进程。它可以让多个进程操作同一块内存区域,并且除了物理内存限制,没有其他大小限制。相对于 Linux 的共享内存,Ashmem 对内存的管理更加精细化,并且添加了互斥锁Java 层在使用时需要用到 MemoryFile,它封装了 native 代码。

Java 层使用匿名共享内存的4个点:

  1. 通过 MemoryFile 开辟内存空间,获得 FileDescriptor
  2. FileDescriptor 传递给其他进程;
  3. 往共享内存写入数据;
  4. 从共享内存读取数据。

下面用一个例子介绍匿名共享内存的使用,假设需要开辟一段共享内存,写入一些数据,再在另外一个进程读取这段数据。

2. 创建 MemoryFile 和 数据写入

/**
* 需要写入到共享内存中的数据
*/
private val bytes = "落霞与孤鹜齐飞,秋水共长天一色。".toByteArray()/**
* 创建 MemoryFile 并返回 ParcelFileDescriptor
*/
private fun createMemoryFile(): ParcelFileDescriptor? {// 创建 MemoryFile 对象,1024 是最大占用内存的大小。val file = MemoryFile("TestAshmemFile", 1024)// 获取文件描述符,因为方法被标注为 @hide,只能反射获取val descriptor = invokeMethod("getFileDescriptor", file) as? FileDescriptor// 如果获取失败,返回if (descriptor == null) {Log.i("ZHP", "获取匿名共享内存的 FileDescriptor 失败")return null}// 往共享内存中写入数据file.writeBytes(bytes, 0, 0, bytes.size)// 因为要跨进程传递,需要序列化 FileDescriptorreturn ParcelFileDescriptor.dup(descriptor)
}/**
* 通过反射执行 obj.name() 方法
*/
private fun invokeMethod(name: String, obj: Any): Any? {val method = obj.javaClass.getDeclaredMethod(name)return method.invoke(obj)
}

MemoryFile 有两个构造方法,上面是一种,另一种是根据已有的 FileDescriptor 创建。 MemoryFile 创建时指定的大小并不是实际占用的物理内存大小,实际占用内存大小由写入的数据决定,但不能超过指定的大小。

3. 将文件描述符传递到其他进程

这里选择用 Binder 传递 ParcelFileDescriptor。 我们定义一个 Code,用于 C/S 两端通信确定事件:

/**
* 两个进程在传递 FileDescriptor 时用到的 Code。
*/
const val MY_TRANSACT_CODE = 920511

再在需要的地方 bindService

// 创建服务进程
val intent = Intent(this, MyService::class.java)
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)

bind 成功之后将 文件描述符数据大小 序列化,然后通过 Binder 传递到 Service 进程:

private val serviceConnection = object: ServiceConnection {override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {if (binder == null) {return}// 创建 MemoryFile,并拿到 ParcelFileDescriptorval descriptor = createMemoryFile() ?: return// 传递 FileDescriptor 和 共享内存中数据的大小val sendData = Parcel.obtain()sendData.writeParcelable(descriptor, 0)sendData.writeInt(bytes.size)// 保存对方进程的返回值val reply = Parcel.obtain()// 开始跨进程传递binder.transact(MY_TRANSACT_CODE, sendData, reply, 0)// 读取 Binder 执行的结果val msg = reply.readString()Log.i("ZHP", "Binder 执行结果是:「$msg」")}override fun onServiceDisconnected(name: ComponentName?) {}
}

两个进程的文件描述符指向同一个文件结构体,文件结构体指向了一片内存共享区域(ASMA),使得两个文件描述符对应到同一片ASMA中。

4. 在其他进程接收 FileDescriptor 并读取数据

先定义一个 MyService 用于开启子进程:

class MyService : Service() {private val binder by lazy { MyBinder() }override fun onBind(intent: Intent) = binder
}

再实现具体的 MyBinder 类,主要包含3个步骤:

  1. 从序列化数据中读取 FileDescriptor 和 共享内存中保存的数据大小;
  2. 根据 FileDescriptor 创建 FileInputStream
  3. 读取共享内存中的数据。
/**
* 这里不必使用 AIDL,继承 Binder 类 重写 onTransact 即可。
*/
class MyBinder: Binder() {/*** 文件描述符 和 数据大小 通过 data 传入。*/override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {val parent = super.onTransact(code, data, reply, flags)if (code != MY_TRANSACT_CODE && code != 931114) {return parent}// 读取 ParcelFileDescriptor 并转为 FileDescriptorval pfd = data.readParcelable<ParcelFileDescriptor>(javaClass.classLoader)if (pfd == null) {return parent}val descriptor = pfd.fileDescriptor// 读取共享内存中数据的大小val size = data.readInt()// 根据 FileDescriptor 创建 InputStreamval input = FileInputStream(descriptor)// 从 共享内存 中读取字节,并转为文字val bytes = input.readBytes()val message = String(bytes, 0, size, Charsets.UTF_8)Log.i("ZHP", "读取到另外一个进程写入的字符串:「$message」")// 回复调用进程reply?.writeString("Server 端收到 FileDescriptor, 并且从共享内存中读到了:「$message」")return true}
}

这里拿到 FileDescriptor 后不仅可以读也能写入数据,还可以再创建一个 MemoryFile 对象。

这就是Android匿名共享内存的使用啦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/84099.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

医药行业安全生产信息化建设分享

随着科技的快速发展和全球化进程的推进&#xff0c;医药行业作为人类健康和安全的重要组成部分&#xff0c;面临着日益严峻的安全生产挑战。近年来&#xff0c;医药企业对于安全生产的需求越来越强烈&#xff0c;安全生产信息化建设成为了医药行业发展的重要趋势。本文将探讨医…

短剧解说小程序搭建,短剧解说小程序源码

短剧解说小程序搭建&#xff0c;短剧解说小程序源码 可定制开发小程序&#xff0c;H5&#xff0c;APP等系统 有需要可定制可出源码&#xff0c;这个是啥你懂的(VVVVVVVVVVV)&#xff1a;二五四九七八九零五九 需要源码或搭建可看上面的数字信息 短剧解说小程序搭建 小程序使用…

nvm使用的注意事项和常用命令。

nvm官网下载地址&#xff1a;nvm文档手册 - nvm是一个nodejs版本管理工具 - nvm中文网 (uihtm.com) 参考网址&#xff1a;(14 封私信 / 80 条消息) 如何通过 nvm 安装多版本 nodejs&#xff1f;npm 安装失败了怎么办&#xff1f; - 知乎 (zhihu.com) nvm目录下&#xff0c;修…

自定义协议、序列化与反序列化

在编写TCP和UDP程序的时候&#xff0c;我们很自然的就使用了读取的函数对数据进行获取&#xff0c;对于UDP来说提供的是无连接的以数据报的形式进行传输&#xff0c;对于TCP来说是面向数据流的&#xff0c;在之前的程序中我们只是进行了读取的操作&#xff0c;但是并没有对读取…

修复Ripro主题扫码后空白或null或array的问题

WordPress Ripro主题使用的人比较多&#xff0c;绝大部分用的盗版主题&#xff0c;RiPro主题使用虎皮椒V3支付接口在使用弹窗支付的时候&#xff0c;微信或者支付宝扫码后出现null或array&#xff0c;会发生在ripro所有版本中。下面是修复方法&#xff1a; 打开ripro主题inc目…

代码大全阅读随笔(七)

循环控制 循环控制会出现什么样的错误&#xff0c;任何一种答案都可以归结到下面所说的问题之一&#xff1a;忽略或者错误的对循环执行初始化&#xff0c;忽略了对累加变量或者其他与循环有关变量执行初始化&#xff0c;不正确的嵌套&#xff0c;不正确的循环终止&#xff0c;忽…

安科瑞为工业能效提升行动计划提供EMS解决方案-安科瑞黄安南

摘要: 2022年6月29日工信部、发改委、财政部、生态环境部、国资委、市场监管总局六部门联合下发《关于印发工业能效提升行动计划的通知》&#xff08;工信部联节〔2022〕76号&#xff0c;以下简称《行动计划》&#xff09;&#xff0c;主要目的是为了提高工业领域能源利用效率&…

Hbase分布式集群部署

目录 一、环境说明 二、部署Hbase 2.1 解压Hbase 2.2 移动解压包 2.3 修改 hbase-env.sh文件 2.4 修改环境变量 2.5 修改hbase-site.xml文件 2.6 修改regionservers 文件 2.7 分发hbase 2.7.1 分发hbase包 2.7.2 分发环境配置 2.8 启动hbase服务 2.8.1 环境生效 …

特殊时间(蓝桥杯)

特殊时间 问题描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 2022年2月22日22:20 是一个很有意义的时间, 年份为 2022 , 由 3 个 2 和 1 个 0 组成, 如果将月和日写成 4 位, 为 0222 , 也是由 3 个 2 和 1 个 0 组 成…

Tungsten Fabric数据量过大问题处理初探

开源SDN系统Tungsten Fabric面临数据产生过多问题。 经排查&#xff0c;产生数据多出自analytics组件的Cassandra数据库()。很多分析数据会存储至Cassandra库&#xff0c;并持久化处理。 没有特殊调整的话&#xff0c;目录在 /var/lib/docker/volumes/analytics_database_an…

抗锯齿的线

抗锯齿的线 右下角的时候h是0,到顶部 h是1&#xff0c;然后中间y相距4个像素&#xff0c;那dy就是0.25 如果让h abs(fract(h - 0.5) - 0.5) 中间一行0.5&#xff0c;第一行 第三行都是0.25&#xff0c;两端都是0 根据插值来看 这里是 如果用h/dy 那么第一行以上&#xff0…

【ROS入门】创建工作空间与功能包

文章结构 工作空间文件结构创建工作空间流程创建工作空间编译工作空间设置环境变量/创建功能包创建功能包编译功能包 检查环境变量 工作空间文件结构 工作空间(workspace)是一个存放工程开发相关文件的文件夹&#xff0c;类似于在windows中使用IDE创建的工程。主要分为以下四个…

276_C++_HTTP请求处理的内部实现,具体处理逻辑在其中完成

这个函数是HTTP请求处理的内部实现,具体处理逻辑在其中完成。 根据参数的值和请求的类型,它会处理不同的情况,包括错误处理、身份验证和设置回调函数等操作。 ok这段代码主要实现了一个HTTP服务器的请求处理逻辑,处理IPC升级相关的请求。 具体的处理逻辑和细节需要查看代码…

tree型数组平铺,一维数组结构tree型

一维数组结构成tree型关键是每个一维数字里面必须有一个自己唯一识别的标识&#xff08;id&#xff09;和父级唯一识别的表示 &#xff08;pid&#xff09; function oneTotree(data,pid,idKey,pidKey){ let node[] pid pid||"" //这里的值是以哪个pid的值为根节点…

【element-ui】form表单动态修改rules校验项

在项目开发过程中&#xff0c;该页面有暂存和提交两个按钮&#xff0c;其中暂存和提交必填项校验不一样&#xff0c;此时需要动态增减必填项校验 &#xff0c;解决方法如下&#xff1a; 增加rules校验项 this.$set(this.formRules,name,[{required:true,message:请输入名称,t…

【python】anaconda使用指南

安装anaconda 访问官方 官网链接注册并登陆安装 无脑下一步即可配置path D:\ProgramData\anaconda3D:\ProgramData\anaconda3\ScriptsD:\ProgramData\anaconda3\Library\binD:\ProgramData\anaconda3\Library\mingw-w64\bin 进入anaconda环境 # 查询版本 conda --version# …

Layui快速入门之第十三节 日期与时间选择器

目录 一&#xff1a;基本用法 API 渲染 属性 弹出提示 2.8 获取实例 2.8 解除实例绑定 2.8 关闭日期面板 2.7 获取某月的最后一天 二&#xff1a;常规用法 三&#xff1a;多类型选择器 四&#xff1a;范围选择 五&#xff1a;直接静态显示 六&#xff1a;更多功能…

断点测试怎么做?一文教你用Charles 工具做好接口测试!

在测试工作过程中&#xff0c;我们经常会在程序的某一行或者某一环节设置断点&#xff0c;在程序请求的过程中&#xff0c;修改断点处的参数、请求或者响应&#xff0c;借此定位问题&#xff0c;这就是所谓的断点测试。这类断点测试主要用于接口测试。 断点测试可以通过查看接…

Dubbo常考知识点

Dubbo常考知识点 Dubbo⽀持哪些负载均衡策略Dubbo是如何完成服务导出的&#xff1f;Dubbo是如何完成服务引⼊的&#xff1f;Dubbo的架构设计是怎样的&#xff1f; Dubbo⽀持哪些负载均衡策略 随机&#xff1a;从多个服务提供者随机选择⼀个来处理本次请求&#xff0c;调⽤量越…

C语言关于自定义字符函数和字符串函数的相关笔试题(找工作必看)

本篇字符函数和字符串函数 求字符串长度 strlen 长度不受限制的字符串函数 strcpy strcat strcmp 长度受限制的字符串函数介绍 strncpy strncat strncmp 字符串查找 strstr strtok 错误信息报告 strerror 内存操作函数 memcpy memmove memset memcmp 在我们笔试时&#xff0c;很…