引入
在使用Intent传输数据的时候,数据大小超过1M就会报错。
崩溃异常:
Exception: TransactionTooLargeException
接下来来分析下为什么页面数据传输会有这个量的限制以及这个限制的大小具体是多少。
道Context和Activity都含有startActivity,但两者最终都调用了Activity中的startActivity;
而startActivity最终会调用自身的startActivityForResult;
然后系统会调用Instrumentation中的execStartActivity方法;
接着调用了ActivityManger.getService().startActivity ,getService返回的是系统进程中的AMS在app进程中的binder代理;
其实就是App进程调用AMS进程中的方法了。就是系统进程和app应用进程间的数据交互了。所以Intent的数据都要带入AMS,就是Binder通信了。
探究Intent数据大小限制,就是弄清楚Binder的数据传输问题。
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。