Android 的 匿名共享内存(Ashmem) 基于 Linux 的共享内存,都是在临时文件系统(tmpfs)上创建虚拟文件,再映射到不同的进程。它可以让多个进程操作同一块内存区域,并且除了物理内存限制,没有其他大小限制。相对于 Linux 的共享内存,Ashmem 对内存的管理更加精细化,并且添加了互斥锁。Java 层在使用时需要用到 MemoryFile,它封装了 native 代码。 Java 层使用匿名共享内存的4个
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》 浏览器打开:qq.cn.hn/FTe 开源分享
点:
1. 通过 MemoryFile 开辟内存空间,获得 FileDescriptor;
将 FileDescriptor 传递给其他进程;
往共享内存写入数据;
从共享内存读取数据。
下面用一个例子介绍匿名共享内存的使用,假设需要开辟一段共享内存,写入一些数据,再在另外一个进程读取这段数据。
/**
/**
// 获取文件描述符,因为方法被标注为 @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)
}
/**
MemoryFile 有两个构造方法,上面是一种,另一种是根据已有的 FileDescriptor 创建。 MemoryFile 创建时指定的大小并不是实际占用的物理内存大小,实际占用内存大小由写入的数据决定,但不能超过指定的大小。
这里选择用 Binder 传递 ParcelFileDescriptor。 我们定义一个 Code,用于 C/S 两端通信确定事件:
/**
再在需要的地方 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中。
先定义一个 MyService 用于开启子进程:
class MyService : Service() {