我的编程空间,编程开发者的网络收藏夹
学习永远不晚

详解Android Ashmem匿名共享内存

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

详解Android Ashmem匿名共享内存

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()



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)

    // 因为要跨进程传递,需要序列化 FileDescriptor
    return ParcelFileDescriptor.dup(descriptor)
}



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 两端通信确定事件:



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,并拿到 ParcelFileDescriptor
        val 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. 读取共享内存中的数据。



class MyBinder: Binder() {

    
    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 并转为 FileDescriptor
        val pfd = data.readParcelable<ParcelFileDescriptor>(javaClass.classLoader)
        if (pfd == null) {
            return parent
        }
        val descriptor = pfd.fileDescriptor

        // 读取共享内存中数据的大小
        val size = data.readInt()

        // 根据 FileDescriptor 创建 InputStream
        val 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 Ashmem匿名共享内存的详细内容,更多关于Android Ashmem匿名共享内存的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

详解Android Ashmem匿名共享内存

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

【Linux】进程间通信(万字详解)—— 匿名管道 | 命名管道 | System V | 共享内存

🌈欢迎来到Linux专栏~~进程通信 (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort目前状态:大三非科班啃C++中🌍博客主页:张小姐的猫~江湖背景快上车🚘,握好方向盘跟我有一
2023-08-22

Linux共享内存实现机制的详解

Linux共享内存实现机制的详解 内存共享: 两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步
2022-06-04

Python进程间通信之共享内存详解

前一篇博客说了怎样通过命名管道实现进程间通信,但是要在windows是使用命名管道,需要使用python调研windows api,太麻烦,于是想到是不是可以通过共享内存的方式来实现。查了一下,Python中可以使用mmap模块来实现这一功
2022-06-04

OpenMP共享内存的并行编程框架入门详解

这篇文章主要为大家介绍了OpenMP共享内存的并行编程框架入门详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-16

【Java基础教程】(七)面向对象篇 · 第一讲:上干货!面向对象的特性、类与对象、内存结构引用分析、垃圾收集器 GC处理、封装性详解、构造方法、匿名对象、简单 Java 类~

Java基础教程之面向对象 · 第一讲 🍉 篇章介绍本节学习目标1️⃣ 面向对象的三个特性2️⃣ 类与对象2.1 基本概念2.2 定义 3️⃣ 引用分析🔍 关于`垃圾收集器 GC`处理的介绍
2023-08-19

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录