背景
intent在页面间传递数据是有大小限制的,当我们用Intent传输大数据时(超过1MB)程序就报TransactionTooLargeException错误
系统进程中的AMS集中负责管理所有进程中的Activity。app进程与系统进程需要进行双向通信。比如打开一个新的Activity,则需要调用系统进程AMS中的方法进行实现,AMS等实现完毕需要回调app进程中的相关方法进行具体activity生命周期的回调。
所以我们在intent中携带的数据也要从APP进程传输到AMS进程,再由AMS进程传输到目标Activity所在进程。
、 binder数据传输
普通的由Zygote孵化而来的用户进程,所映射的Binder内存大小是不到1M的,准确说是 110241024) - (4096 *2)
这个限制定义在frameworks/native/libs/binder/processState.cpp类中,如果传输说句超过这个大小,系统就会报错,因为Binder本身就是为了进程间频繁而灵活的通信所设计的,并不是为了拷贝大数据而使用的
总结
startActivity携带的数据会经过BInder内核再传递到目标Activity中去,因为binder映射内存的限制,所以startActivity也就会这个限制了。
替代方案
1、 写入临时文件或者数据库,通过FileProvider将该文件或者数据库通过Uri发送至目标。
一般适用于不同进程,比如分离进程的UI和后台服务,或不同的App之间。之所以采用FileProvider是因为7.0以后,对分享本App文件存在着严格的权限检查。
2、 通过设置静态类中的静态变量进行数据交换。一般适用于同一进程内,这样本质上数据在内存中只存在一份,通过静态类进行传递。需要注意的是进行数据校对,以防多线程操作导致的数据显示混乱。
不用ProcessState来初始化Binder服务,来突破1M-8KB的限制?
可以。Binder服务的初始化有两步
- open打开Binder驱动
- mmap在Binder驱动中申请内核空间内存
所以只要手写open,mmap就可以轻松突破这个限制。源码中已经给了类似的例子。
Binder驱动不怕我们传递一个超级大的数字进去吗
在Binder驱动中mmap的具体实现中还有一个4M的限制
对于oneway的Binder调用,可申请内核空间,最大上限是buffer_size的一半,也就是mmap时候传递的值的一半。
猜测原因:Binder调用中同步调用优先级大于oneway(异步)的调用,为了充分满足同步调用的内存需要,所以将oneway调用的内存限制到申请内存上限的一半。
一次Binder通信最大可以传输多大的数据
自己写的APP能否突破1M-8KB的限制
答案是理论上可以,但是不建议这样子操作,因为Binder驱动中并没有对open,mmap有调用次数的限制,App可以通过JNI调用open,mmap来突破这个限制,但是会对当前正在进行Binder调用的APP造成不可想象问题,当然可以先close Binder驱动。但是一旦这个APP没有Binder通信了,这个APP就不能正常使用了,APP和其他应用,AMS,WMS的交互可都是依赖于Binder通信,所以还是那句话,无Binder无Android。