深入浅出安卓匿名共享内存

341 阅读3分钟

深入浅出安卓匿名共享内存

一、匿名共享内存的本质

你可以把匿名共享内存(Ashmem)想象成一个"魔法黑板":

  • 多个应用可以同时在这块黑板上读写
  • 不需要知道对方是谁(匿名性)
  • 数据直接保存在内存中,速度极快
  • 系统会自动管理这块黑板的生命周期

二、底层实现原理

  1. 内核驱动:Ashmem实际上是Linux内核的一个驱动模块
  2. 内存映射:通过mmap系统调用将内存映射到进程地址空间
  3. 文件描述符传递:使用Binder机制传递文件描述符实现跨进程共享

三、详细工作流程

  1. 创建阶段

    • 调用ashmem_create_region创建共享内存区域
    • 返回一个文件描述符(fd)作为操作句柄
  2. 映射阶段

    • 使用mmap将共享内存映射到进程地址空间
    • 获得可直接操作的内存指针
  3. 共享阶段

    • 通过Binder将fd传递给其他进程
    • 接收方同样使用mmap映射这块内存

四、完整代码示例

// 创建端
fun createSharedMemory(): ParcelFileDescriptor {
    // 1. 创建共享内存(1KB大小)
    val sharedMemory = SharedMemory.create("demo_memory", 1024)
    
    // 2. 映射为可读写
    val buffer = sharedMemory.mapReadWrite()
    
    // 3. 写入数据
    buffer.putInt(0, 123)  // 在位置0写入整数123
    buffer.putString(4, "Hello")  // 在位置4写入字符串
    
    // 4. 返回文件描述符
    return sharedMemory.fd
}

// 接收端
fun readSharedMemory(fd: ParcelFileDescriptor) {
    // 1. 从文件描述符创建SharedMemory
    val sharedMemory = SharedMemory.fromFileDescriptor(fd.fileDescriptor)
    
    // 2. 映射为只读(安全考虑)
    val buffer = sharedMemory.mapReadOnly()
    
    // 3. 读取数据
    val number = buffer.getInt(0)  // 读取位置0的整数
    val text = buffer.getString(4) // 读取位置4的字符串
    
    Log.d("Ashmem", "收到数据: $number, $text")
}

五、性能优化技巧

  1. 适当大小:不要分配过大内存,按需分配
  2. 合理映射:只在需要时映射,用完及时unmap
  3. 数据对齐:按4字节或8字节对齐提高访问效率
  4. 批量操作:尽量减少小数据量的频繁读写

六、高级特性

  1. 内存回收

    • 使用SharedMemory.setProtect()设置内存保护
    • 可标记为"可丢弃",系统在内存紧张时优先回收
  2. 原子操作

    • 通过内存屏障保证多进程读写一致性
    • 可使用AtomicInteger等原子类
  3. 类型安全

    • 推荐使用Parcelable或ProtoBuf结构化数据
    • 避免直接操作原始内存

七、常见问题解答

Q: 和普通文件共享有什么区别? A: 完全内存操作,不经过磁盘IO,速度快10倍以上

Q: 最多能共享多大内存? A: 理论上是系统内存限制,但建议不超过几十MB

Q: 如何保证线程安全? A: 需要自行加锁,或使用原子变量

Q: 数据会持久化吗? A: 不会,进程退出后内存会被回收

八、实际应用案例

  1. 跨进程图片传输

    • 相机应用捕获照片后直接写入Ashmem
    • 图片编辑应用直接从内存读取,避免文件IO
  2. 实时数据共享

    • 传感器数据实时共享给多个分析应用
    • 游戏状态同步
  3. 插件化框架

    • 宿主与插件间高效通信
    • 共享大型资源数据

记住:能力越大责任越大,匿名共享内存虽强大,但要注意安全性和资源管理!