热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

详解AndroidAshmem匿名共享内存

这篇文章主要介绍了AndroidAshmem匿名共享内存的相关资料,帮助大家更好的理解和学习使用Android开发,感兴趣的朋友可以了解下

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)

    // 因为要跨进程传递,需要序列化 FileDescriptor
    return 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,并拿到 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. 读取共享内存中的数据。

/**
 * 这里不必使用 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 并转为 FileDescriptor
        val pfd = data.readParcelable(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匿名共享内存的资料请关注其它相关文章!


推荐阅读
  • LCUI 2.1.0 版本现已推出,这是一个用 C 语言编写的图形用户界面开发库,适合创建轻量级的桌面应用程序。此次更新包括多项修复和功能增强,并正式宣布将启动 Android 支持的开发计划。 ... [详细]
  • Ubuntu GamePack:专为游戏爱好者打造的Linux发行版
    随着Linux系统在游戏领域的应用越来越广泛,许多Linux用户开始寻求在自己的系统上畅玩游戏的方法。UALinux,一家致力于推广GNU/Linux使用的乌克兰公司,推出了基于Ubuntu 16.04的Ubuntu GamePack,旨在为Linux用户提供一个游戏友好型的操作环境。 ... [详细]
  • Python数据类型6 字典
    字典Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。但它是无序的,包 ... [详细]
  • 本文详细介绍了ActivityManagerService (AMS) 的工作原理及其在Android系统中的重要角色。AMS作为system_server进程的一部分,在系统启动时加载,负责管理和协调应用程序中的Activity和服务(Service)。文章将通过具体的接口图和通信流程,帮助读者更好地理解AMS的工作机制。 ... [详细]
  • 题目 CF1245F: 清理春天的数学挑战 描述了一个数学问题:给定一个区间 [L, R](0 ≤ L, R ≤ 10^9),求该区间内满足 x + y = x ∧ y 的数对 (x, y) 的总数。 ... [详细]
  • 安装双硬盘对电脑有何益处?
    面对日益增长的数据存储需求,仅通过更换更大容量的硬盘来解决空间问题并非唯一途径。本文探讨了在同一台计算机上安装两个硬盘的可能性及其带来的多种优势。 ... [详细]
  • 字节跳动夏季招聘面试经验分享
    本文详细记录了字节跳动夏季招聘的面试经历,涵盖了一、二、三轮面试的技术问题及项目讨论,旨在为准备类似面试的求职者提供参考。 ... [详细]
  • 利用SSH隧道实现外网对局域网机器的安全访问
    本文探讨了一种常见的网络配置问题及其解决方案,即如何在外网环境下安全地访问位于局域网内的计算机。特别介绍了使用SSH反向隧道技术来实现这一目标的具体步骤和注意事项。 ... [详细]
  • 本文探讨了Web开发与游戏开发之间的主要区别,旨在帮助开发者更好地理解两种开发领域的特性和需求。文章基于作者的实际经验和网络资料整理而成。 ... [详细]
  • 解决CentOS 7.5中无法通过man命令查询C语言库函数的问题
    本文描述了在CentOS 7.5操作系统上,使用man命令查询C语言库函数时遇到的问题,并提供了详细的解决方案。 ... [详细]
  • 在Linux系统上构建Web服务器的详细步骤
    本文详细介绍了如何在Linux系统上搭建Web服务器的过程,包括安装Apache、PHP和MySQL等关键组件,以及遇到的一些常见问题及其解决方案。 ... [详细]
  • 本文将详细介绍如何在ThinkPHP6框架中实现多数据库的部署,包括读写分离的策略,以及如何通过负载均衡和MySQL同步技术优化数据库性能。 ... [详细]
  • 在安装Ubuntu 12.10并尝试安装VMware Tools时,遇到了一个常见的错误提示:指定的路径不是有效的3.5.0-17-generic内核头文件路径。本文将提供解决这一问题的具体步骤。 ... [详细]
  • 如何在Linux中实现字符设备控制
    本文详细探讨了在Linux环境下控制字符设备的方法,包括蜂鸣器和模数转换器(ADC)的实际操作案例。对于开发者来说,了解这些基础知识对于嵌入式系统的开发尤为重要。 ... [详细]
  • Linux中为何使用chmod 777设置最大文件权限
    在Linux系统管理中,设置文件权限是一个常见的操作。其中,chmod 777命令用于赋予文件所有者、组和其他用户完全控制权限。本文将探讨这一命令背后的原理及其应用。 ... [详细]
author-avatar
回忆寒窗氵之夜_890
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有