引入
在使用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实现数据通信:通过Copy_from_User从用户空间将数据拷贝到内核空间,内核空间的内存和Service服务端的内存通过mmap()映射共享。所以原因就在此:
普通的由Zygote孵化而来的用户进程,所映射的Binder内存大小是不到1M的,准确说是 110241024) - (4096 *2) :这个限制定义在/processState.cpp类中,如果传输说句超过这个大小,系统就会报错,因为Binder本身就是为了进程间频繁而灵活的通信所设计的,并不是为了拷贝大数据而使用的,在内核中,其实也有个限制,是4M。
#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
总结
intent携带的数据会经过Binder内核再传递到目标Activity中去,因为binder映射内存的限制,所以startActivity也就会这个限制了。
解决方案
一、写入临时文件或者数据库,通过FileProvider将该文件或者数据库通过Uri发送至目标。一般适用于不同进程,比如分离进程的UI和后台服务,或不同的App之间。之所以采用FileProvider是因为7.0以后,对分享本App文件存在着严格的权限检查。
二、通过设置静态类中的静态变量进行数据交换。一般适用于同一进程内,这样本质上数据在内存中只存在一份,通过静态类进行传递。需要注意的是进行数据校对,以防多线程操作导致的数据显示混乱。