Pangu-Immortal (Pangu-Immortal) · GitHub

当我们用Intent传输大数据时,有可能会出现错误:

val intent = Intent(this@MainActivity, Main2Activity::class.java)
val data = ByteArray(1024 * 1024)
intent.putExtra("111", data)
startActivity(intent)

如上我们传递了1M大小的数据时,结果程序就一直反复报如下TransactionTooLargeException错误:

但我们平时传递少量数据的时候是没问题的。由此得知,通过intent在页面间传递数据是有大小限制的。本文我们就来分析下为什么页面数据传输会有这个量的限制以及这个限制的大小具体是多少 ?


StartActivity流程探究

首先我们知道Context和Activity都含有startActivity,但两者最终都调用了Activity中的startActivity:

@Override
public void startActivity(Intent intent, @Nullable Bundle options) {if (options != null) {startActivityForResult(intent, -1, options);} else {// Note we want to go through this call for compatibility with// applications that may have overridden the method.startActivityForResult(intent, -1);}
}

而startActivity最终会调用自身的startActivityForResult,省略了嵌套activity的代码:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {options = transferSpringboardActivityOptions(options);Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);if (ar != null) {mMainThread.sendActivityResult(mToken, mEmbeddedID, requestCode, ar.getResultCode(),ar.getResultData());}if (requestCode >= 0) {// If this start is requesting a result, we can avoid making// the activity visible until the result is received.  Setting// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the// activity hidden during this time, to avoid flickering.// This can only be done when a result is requested because// that guarantees we will get information back when the// activity is finished, no matter what happens to it.mStartedActivity = true;}cancelInputsAndStartExitTransition(options);
}

然后系统会调用Instrumentation中的execStartActivity方法:

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {IApplicationThread whoThread = (IApplicationThread) contextThread;...try {intent.migrateExtraStreamToClipData();intent.prepareToLeaveProcess(who);int result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target != null ? target.mEmbeddedID : null,requestCode, 0, null, options);checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;
}

接着调用了ActivityManger.getService().startActivity ,getService返回的是系统进程中的AMS在app进程中的binder代理:

/*** @hide*/
public static IActivityManager getService() {return IActivityManagerSingleton.get();
}private static final Singleton<IActivityManager> IActivityManagerSingleton =new Singleton<IActivityManager>() {@Overrideprotected IActivityManager create() {final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);final IActivityManager am = IActivityManager.Stub.asInterface(b);return am;}
};

接下来就是App进程调用AMS进程中的方法了。简单来说,系统进程中的AMS集中负责管理所有进程中的Activity。app进程与系统进程需要进行双向通信。

比如打开一个新的Activity,则需要调用系统进程AMS中的方法进行实现,AMS等实现完毕需要回调app进程中的相关方法进行具体activity生命周期的回调。

所以我们在intent中携带的数据也要从APP进程传输到AMS进程,再由AMS进程传输到目标Activity所在进程。有同学可能由疑问了,目标Acitivity所在进程不就是APP进程吗?

其实不是的,我们可以在Manifest.xml中设置android:process属性来为Activity, Service等指定单独的进程,所以Activity的startActivity方法是原生支持跨进程通信的。

接下来简单分析下binder机制。


binder介绍

普通的由Zygote孵化而来的用户进程,所映射的Binder内存大小是不到1M的,准确说是 110241024) - (4096 *2) :这个限制定义在

frameworks/native/libs/binder/processState.cpp类中,如果传输说句超过这个大小,系统就会报错,因为Binder本身就是为了进程间频繁而灵活的通信所设计的,并不是为了拷贝大数据而使用的:

#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))

并可以通过cat proc/[pid]/maps命令查看到。

而在内核中,其实也有个限制,是4M,不过由于APP中已经限制了不到1M,这里的限制似乎也没多大用途:

static int binder_mmap(struct file *filp, struct vm_area_struct *vma){int ret;struct vm_struct *area;struct binder_proc *proc = filp->private_data;const char *failure_string;struct binder_buffer *buffer;//限制不能超过4Mif ((vma->vm_end - vma->vm_start) > SZ_4M)vma->vm_end = vma->vm_start + SZ_4M;。。。
}

其实在TransactionTooLargeException中也提到了这个:

The Binder transaction buffer has a limited fixed size, currently 1Mb, which

is shared by all transactions in progress for the process. Consequently this

exception can be thrown when there are many transactions in progress even when

most of the individual transactions are of moderate size.

只不过不是正好1MB,而是比1MB略小的值。


小结

至此我们来解答开头提出的问题,startActivity携带的数据会经过BInder内核再传递到目标Activity中去,因为binder映射内存的限制,所以startActivity也就会这个限制了。

替代方案

一、写入临时文件或者数据库,通过FileProvider将该文件或者数据库通过Uri发送至目标。一般适用于不同进程,比如分离进程的UI和后台服务,或不同的App之间。之所以采用FileProvider是因为7.0以后,对分享本App文件存在着严格的权限检查。

二、通过设置静态类中的静态变量进行数据交换。一般适用于同一进程内,这样本质上数据在内存中只存在一份,通过静态类进行传递。需要注意的是进行数据校对,以防多线程Data Racer出现导致的数据显示混乱。

Pangu-Immortal (Pangu-Immortal) · GitHub

参考资料

听说你Binder机制学的不错,来面试下这几个问题(一)

https://www.jianshu.com/p/adaa1a39a274

源码分析:startActivity流程

https://www.jianshu.com/p/dc6b0ead30aa


文中提到一个重点类

这个限制定义在frameworks/native/libs/binder/processState.cpp类中

很多开发者可能不知道去哪里看这个类,这里跟大家分享一个非常快速便捷的查看方式,比较适合偶尔查找一两个类:

直接进入搜索就好了,包含各个版本的源码:

如果你指定了某个具体的版本,还有非常便利的提示:


生活中信息交流的核心是数据,Android提供了多种数据存储的方式,文件存储数据,SharedPreferences存储数据,SQLite数据库存储数据,ContentProvider存储数据,根据不同的数据文件选择合适的存储方式是开发者所具备的基本技能之一。

Pangu-Immortal (Pangu-Immortal) · GitHub

从Zygote孵化frameworks进程,分析StartActivity流程中intent传递数据的最大值。相关推荐

  1. 【Windows 逆向】OD 调试器工具 ( CE 工具通过查找访问的方式找到子弹数据基地址 | 使用 OD 工具附加游戏进程 | 在 OD 工具中查看子弹数据地址 | 推荐 )

    文章目录 前言 一.CE 工具通过查找访问的方式找到子弹数据基地址 二.使用 OD 工具附加游戏进程 三.在 OD 工具中查看 058E2F1C 地址数据 前言 上一篇博客 [Windows 逆向]O ...

  2. Android底层隐私数据,Android Intent传递数据底层分析详细介绍_Android_脚本之家

    Android  Intent传递数据底层分析详细介绍 我们知道在Activity切换时,如果需要向下一个ActivityB传递数据,可以借助Intent对象的putExtra方法. 但是不知各位有没 ...

  3. android中intent放数据类型,Android Intent传递数据底层分析详细介绍

    Android  Intent传递数据底层分析详细介绍 我们知道在Activity切换时,如果需要向下一个ActivityB传递数据,可以借助Intent对象的putExtra方法. 但是不知各位有没 ...

  4. 用地图说话 在商业分析与演示中运用Excel数据地图 全彩

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 用地图说 ...

  5. Nifi 怀疑出个bug 流程中的实时数据结果痕迹没有不是实时的,是之前的。以及相应解决办法。

    1 在 Nifi 中做数据处理,有一个半小时之前执行过的流程,因为没设置好执行的时间,结果把读的数据库的表持续读成数据流 流到文件里,结果生成大量文件,一个文件就存着一张表的结果. 2 然后过了三十分 ...

  6. Android5.0源码分析—— Zygote进程分析

    1      Zygote简介 Android的应用程序一般都是由Java语言编写而成的,这样的应用程序需要运行在独自的Dalvik虚拟机之上(当然,5.0好像默认了ART了).但是,如果在每一个进程 ...

  7. 探索startActivity流程及在Activity间是如何传递Intent的

    在activity中intent到底是怎么传递的,而且还可以跨进程甚至跨app来传递,下面我们从源码层面探索一下 从startActivity开始,源码如下: @Override public voi ...

  8. 【Windows 逆向】OD 调试器工具 ( CE 工具通过查找访问的方式找到子弹数据基地址 | 使用 OD 工具附加游戏进程 | 在 OD 工具中查看 05869544 地址数据 | 仅做参考 )

    文章目录 一.CE 工具通过查找访问的方式找到子弹数据基地址 二.使用 OD 工具附加游戏进程 三.在 OD 工具中查看 05869544 地址数据 一.CE 工具通过查找访问的方式找到子弹数据基地址 ...

  9. 用WM_COPYDATA消息来实现两个进程之间传递数据

    文着重讲述了如果用WM_COPYDATA消息来实现两个进程之间传递数据. 进程之间通讯的几种方法: 在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯.常用的方法有   1.使用内存映 ...

最新文章

  1. Python练习-基于socket的FTPServer
  2. PowerShell管理Azure
  3. ViewData 和 ViewBag 到底有什么区别?
  4. IOC操作Bean管理XML方式(注入集合类型属性)
  5. XP停止服务,共建网络安全大环境
  6. CentOS安装及注意事项
  7. nyoj 题目20 吝啬的国度
  8. [转] PHP:自己动手制作伪原创程序
  9. 百灵欧拓O2O移动广告平台
  10. AD20中PCB设计流程
  11. 转:gp88写频教程
  12. 计算机视觉 | 1. 一切的基础: 灰度图像 (读取,转换,像素定位)
  13. Oracle 登陆数据库的方式
  14. 我平时整理的一个生成机器码的类(转载)
  15. 设有一数据库,包括四个表:学生表(Student)、课程表(Course)、成绩表(Score)以及教师信息表(Teacher)
  16. Python文件操作与PDF处理
  17. html 合并单元格 步骤总结
  18. Unitree Go1——开发指南
  19. USB接口禁用小工具v1.0.1
  20. 目标检测标注工具(可自定义生成标签模板)

热门文章

  1. Integer的==问题
  2. 数据库四种事务隔离级别详解
  3. 视频目标跟踪算法综述
  4. 《实战突击.php项目开发案例整合》.(明日科技).[PDF]ckook
  5. CSS文件添加 @charset utf-8; 可能会引起样式在IE6下失效
  6. 视图系统CBV 和 response
  7. Dubbo详解-说明(一)
  8. memcache调整value大小限制
  9. buildroot的使用简介【转】
  10. Mybatis接口注解开发