当前位置: 首页 > news >正文

Android匿名共享内存突破Binder传递大小限制

一、Binder 传输限制

在 Android 中,Activity 之间通信(无论是通过 Intent 的 extras 还是其他基于 Binder 的 IPC)底层都依赖于 Binder 驱动。Binder 被优化用于传输小型、高频的控制命令,而不是大数据块。

  • 限制大小: 在一次 Binder 事务(Transaction)中,可以传输的数据量有一个严格的限制。这个限制因 Android 版本和设备而异,但通常约为 1MB(准确值是 1024*1024 - 4096 字节,即 1MB 减去一个页面的大小)。

  • 后果: 如果你尝试通过 Intent.putExtra() 传递一个超过这个限制的 Bitmap 或 Serializable 对象,系统会抛出 TransactionTooLargeException 异常。

二、解决方案:匿名共享内存

Ashmem 是 Linux 内核提供的一个机制,Android 系统利用它来实现进程间共享内存区域。它的核心思想是:

  • 不复制数据: 在进程间传递数据时,不再需要将整个数据块从一个进程的用户空间拷贝到内核空间,再拷贝到另一个进程的用户空间(这是 Binder 的工作方式)。

  • 共享内存区域: 创建一块内存区域,多个进程可以同时映射到自己的虚拟地址空间并直接访问。数据只存在一份。

三、Android 如何利用 Ashmem 来传递大数据?

系统级别的类(如 Bitmap, Bundle, Parcel)在内部智能地使用了 Ashmem。当需要传输的数据很大时,流程如下:

  • 创建 Ashmem 区域: 发送方将大数据(例如一个大 Bitmap 的像素数据)放入一块 Ashmem 中。

  • 传递文件描述符: Ashmem 区域由一个文件描述符(fd)来标识。Binder 非常擅长传递这种小的文件描述符,它可以轻松地穿过 1MB 的限制。

  • 接收方映射内存: 接收方进程通过 Binder 收到这个文件描述符后,将其映射到自己的进程空间。这样,两个进程就可以访问同一块物理内存了。

四、unmarshall 的角色

现在我们来谈谈 unmarshall。这个方法属于 Parcelable 协议的一部分。

  • Parcel: 是一个用于序列化和反序列化的容器,它专门为高性能的 IPC 传输而设计。
  • Parcelable: 是一个接口,对象实现它之后就可以被“打平”放入 Parcel 中进行传输。
  • writeToParcel(Parcel dest, int flags): 对象将自己序列化到 Parcel 中。
  • unmarshall(byte[] data, int offset, int length): 这是一个静态方法,用于将字节数组反序列化,还原成一个 Parcel 对象。

unmarshall 本身并不直接“突破”大小限制。 它的作用是在数据传输的“接收端”进行数据重组。

关键点在于: 如果你直接将大数据写入 Parcel,而 Parcel 的底层实现检测到数据量很大,它会自动选择使用 Ashmem 来存储数据。所以,整个流程是:

  • 发送方: 将你的大对象(例如一个实现了 Parcelable 的包含大数组的类)写入 Parcel。
  • 系统底层: Parcel 内部使用 Ashmem 来存储实际的大数据。
  • 传输: 只有 Ashmem 的文件描述符和小量的元数据通过 Binder 传输。
  • 接收方: 收到一个包含数据的 Bundle 或 Intent,其底层是一个 Parcel。
  • 使用 unmarshall(通常是间接的): 当你调用 Intent.getParcelableExtra() 或 Bundle.getParcelable() 时,系统内部会从接收到的字节数据中调用 Parcel.unmarshall 或其他相关方法,重建出 Parcel 对象,然后再从 Parcel 中反序列化出你的 Java 对象。如果数据存储在 Ashmem 中,unmarshall 过程会正确地映射那块共享内存。

五、实践示例:自己使用 Ashmem

  • AshmemoryTools
/*** 匿名共享内存*/
object AshmemoryTools {private val root_path = AppUtils.getContext().cacheDir.absolutePathprivate val map = ConcurrentHashMap<String, MemoryFile?>()private val sourceMap = ConcurrentHashMap<String, HashMap<String, List<Parcelable>?>>()/*** 写入数据*/fun write(data: HashMap<String, List<Parcelable>?>): String {val path = root_path + System.currentTimeMillis()val parcel = Parcel.obtain().apply {writeInt(data.size)// 写入每个键值对data.forEach { (key, valueList) ->writeString(key)// 写入列表大小writeInt(valueList?.size?:0)// 写入列表中的每个Parcelable对象valueList?.forEach { parcelable ->writeParcelable(parcelable, 0)}}}try {val bytes = parcel.marshall()map[path] = MemoryFile(path, bytes.size).apply {writeBytes(bytes, 0, 0, bytes.size)}} catch (ex: Exception) {ex.printStackTrace()sourceMap[path] = data} finally {parcel.recycle()}return path}/*** 读出数据*/fun read(path: String,classLoader: ClassLoader): HashMap<String, List<Parcelable>?>? {val memoryFile = map[path]try {if (memoryFile != null) {val bytes = ByteArray(memoryFile.length()).also {memoryFile.readBytes(it, 0, 0, it.size)}val parcel = Parcel.obtain()parcel.unmarshall(bytes, 0, bytes.size)parcel.setDataPosition(0)val size = parcel.readInt()return HashMap<String, List<Parcelable>?>(size).apply {repeat(size) {val key = parcel.readString()!!val listSize = parcel.readInt()val valueList = ArrayList<Parcelable>(listSize).apply {repeat(listSize) {add(parcel.readParcelable<Parcelable>(classLoader)!!)}}put(key, valueList)}}}} catch (ex: Exception) {ex.printStackTrace()} finally {memoryFile?.close()}return sourceMap.get(path)}/*** 释放匿名共享内存*/fun delete(path: String) {try {val memoryFile = map[path]memoryFile?.close()sourceMap.clear()} catch (ex: Exception) {ex.printStackTrace()} finally {map.remove(path)}}
}
  • AshmemCache

const val ASHMEM_CACHE_KEY = "ashmem_cache_key"
class AshmemCache() {val dataMap = HashMap<String, List<Parcelable>?>()/*** 加入数据*/fun put(key:String,value:List<Parcelable>?):AshmemCache{dataMap[key] = valuereturn this}/*** 写入数据*/fun write():String{val path = AshmemoryTools.write(dataMap)dataMap.clear()return path}/*** 读出数据*/fun read(path:String,classLoader: ClassLoader){AshmemoryTools.read(path,classLoader)?.apply {val map: HashMap<String, List<Parcelable>?> = thisdataMap.putAll(map)}}fun <T:Parcelable> get(key:String,type: Type): List<T?> {val rawList = dataMap[key] ?: return emptyList()return convertParcelableListByType(rawList as MutableList<Parcelable?>, type)}fun delete(path:String){AshmemoryTools.delete(path)}fun <T : Parcelable?> convertParcelableListByType(parcelableList: MutableList<Parcelable?>,type: Type): MutableList<T?> {val result = mutableListOf<T?>()val targetClass = getRawType(type) as? Class<T> ?: return resultfor (item in parcelableList) {if (targetClass.isInstance(item)) {@Suppress("UNCHECKED_CAST")result.add(item as T)}}return result}private fun getRawType(type: Type): Class<*> {return when (type) {is Class<*> -> typeis ParameterizedType -> getRawType(type.rawType)else -> Any::class.java}}}
  • 使用
//传递数据val path =AshmemCache().put("findVideoBeans", supFriendList1).write()val arouter = ARouter.getInstance().build(FRouter.POND_SNAP_VIDEO_MAIN).withString("ashmemPath", path).navigation()    //获取数据AshmemCache ashmemCache = new AshmemCache();     ashmemCache.read(ashmemCachePath,this.getClass().getClassLoader());List<StickerVideoBean> videoBeans = ashmemCache.get("findVideoBeans",StickerVideoBean.class);//销毁ashmemCache.delete(ashmemCachePath);

六、总结

机制是否突破 1MB-8k 限制原理
传统 Intent/Binder不能数据被完整拷贝两次(进程A -> 内核 -> 进程B),受 Binder 缓冲区大小限制。
Ashmem(匿名共享内存只传递一个小的文件描述符(fd),大数据存储在共享内存中,进程直接访问,无需大块拷贝。
unmarshall 的作用不直接突破它是 Parcelable 反序列化流程的一部分,负责在接收端从字节流(可能来自 Ashmem)重建对象。

因此,Android 系统正是通过匿名共享内存(Ashmem)来巧妙地规避了 Binder 的 1MB 传输限制。而 unmarshall 是这个过程中的一个关键环节,用于在接收方将传输过来的数据(可能存储在 Ashmem 中)重新组装成可用的对象。对于应用开发者来说,通常只需要使用系统提供的 Parcelable 对象(如 Bitmap)或 SharedMemory API 即可自动享受这一机制带来的好处。

http://www.dtcms.com/a/407866.html

相关文章:

  • 网络网站推广首荐乐云seowordpress关键字内链
  • 元气森林宇宙大赛——第五届高校创新挑战赛
  • 51的烧录与调试
  • 如何解决 pip install 安装报错 ModuleNotFoundError: No module named ‘tokenizers’ 问题
  • 数字门店的未来蓝图:从水果店到餐厅再到超市
  • 做动态图片下载哪个网站好百度高级搜索网址
  • Mnn模型转换
  • 在Windows中通过网络共享文件
  • 网站开发入门习题优化大师兑换码
  • 保定网站制作方案四川住房和城乡建设厅进不去网站
  • SPA 路由 fallback 机制 + 304状态码 + 示例
  • 网站怎么设置标题给我一个可以在线观看片
  • Linux学习记录--利用信号量来调度共享资源(2)
  • 管理一个网站的后台怎么做做app要多少钱
  • 自动化测试篇--用例篇
  • 贵阳网络公司网站建设衣服网站模板
  • 基于MATLAB的Copula函数实现示例
  • 攻防世界-Web-simple_js
  • 【Triton 教程】triton_language.ravel
  • 微信网站建设需要那些资料嵌入式软件开发项目
  • 中建一局华江建设有限公司网站类似于wordpress的
  • 学生个人网页设计作品模板肇庆网站快速排名优化
  • 网站优化的核心不包括wordpress商城小程序
  • 整体设计 完整的逻辑链条之11 三转法论驱动的 ISO - 认知融合逻辑系统:从架构映射到自动化缝合的完整设计
  • 网站服务费怎么做分录查网站
  • 项目发布部署
  • 告别字符串拼接繁琐!Java String.format () 实用指南
  • 写小说赚钱的网站温岭市住房和城乡建设局网站
  • 厦门 网站优化宜宾公司做网站
  • 北京网站设计网站公司2021年工程造价信息