Android页面跳转使用Intent 能传递数据大小的问题

本文探讨了Android中Intent在页面跳转时遇到的数据大小限制问题,由于Binder的事务缓冲区限制,可能导致TransactionTooLargeException。Intent通过Bundle传递数据,涉及到序列化,但并非Bundle本身的问题,而是Binder跨进程通信的限制。建议避免通过Intent传递大数据,可选择文件、数据库或EventBus等替代方案。
摘要由CSDN通过智能技术生成

抛出问题

日常开发中我们经常用到的页面跳转传值,而在 Activity 间传递数据,就需要借助 Intent,可以传递基础类型数据或者可序列化的对象数据。平时传递少量数据的时候是没问题的,但是,当传递数据较大的时候会崩溃,就会触发 TransactionTooLargeException 异常。

我们在这里先创建一个困难:

Intent intent = new Intent(context, TestActivity.class);
byte[] data = new byte[1024 * 1024];// 1M数据
intent.putExtra("111", data);
startActivity(intent);

马上得到 Logcat 的反馈:

2021-04-23 10:40:09.253 8037-8037/sun.geoffery.hackdemo I/Timeline: Timeline: Activity_launch_request time:82822722
2021-04-23 10:40:09.256 8037-8037/sun.geoffery.hackdemo E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!  (parcel size = 1049028)
2021-04-23 10:40:09.257 8037-8037/sun.geoffery.hackdemo D/AndroidRuntime: Shutting down VM
    
    
    --------- beginning of crash
2021-04-23 10:40:09.261 8037-8037/sun.geoffery.hackdemo E/AndroidRuntime: FATAL EXCEPTION: main
    Process: sun.geoffery.hackdemo, PID: 8037
    java.lang.RuntimeException: Failure from system
        at android.app.Instrumentation.execStartActivity(Instrumentation.java:1722)
        at android.app.Activity.startActivityForResult(Activity.java:5277)
        at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:675)
        at androidx.core.app.ActivityCompat.startActivityForResult(ActivityCompat.java:234)
        at androidx.fragment.app.FragmentActivity.startActivityFromFragment(FragmentActivity.java:790)
        at androidx.fragment.app.FragmentActivity$HostCallbacks.onStartActivityFromFragment(FragmentActivity.java:932)
        at androidx.fragment.app.Fragment.startActivity(Fragment.java:1257)
        at androidx.fragment.app.Fragment.startActivity(Fragment.java:1245)
        at sun.geoffery.hackdemo.ui.home.HomeFragment$2.onClick(HomeFragment.java:44)
        at android.view.View.performClick(View.java:7307)
        at android.view.View.performClickInternal(View.java:7284)
        at android.view.View.access$3600(View.java:821)
        at android.view.View$PerformClick.run(View.java:28053)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:226)
        at android.app.ActivityThread.main(ActivityThread.java:7592)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
     Caused by: android.os.TransactionTooLargeException: data parcel size 1049028 bytes
        at android.os.BinderProxy.transactNative(Native Method)
        at android.os.BinderProxy.transact(BinderProxy.java:511)
        at android.app.IActivityTaskManager$Stub$Proxy.startActivity(IActivityTaskManager.java:3969)
        at android.app.Instrumentation.execStartActivity(Instrumentation.java:1716)
        at android.app.Activity.startActivityForResult(Activity.java:5277) 
        at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:675) 
        at androidx.core.app.ActivityCompat.startActivityForResult(ActivityCompat.java:234) 
        at androidx.fragment.app.FragmentActivity.startActivityFromFragment(FragmentActivity.java:790) 
        at androidx.fragment.app.FragmentActivity$HostCallbacks.onStartActivityFromFragment(FragmentActivity.java:932) 
        at androidx.fragment.app.Fragment.startActivity(Fragment.java:1257) 
        at androidx.fragment.app.Fragment.startActivity(Fragment.java:1245) 
        at sun.geoffery.hackdemo.ui.home.HomeFragment$2.onClick(HomeFragment.java:44) 
        at android.view.View.performClick(View.java:7307) 
        at android.view.View.performClickInternal(View.java:7284) 
        at android.view.View.access$3600(View.java:821) 
        at android.view.View$PerformClick.run(View.java:28053) 
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:226) 
        at android.app.ActivityThread.main(ActivityThread.java:7592) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950) 

其实这个问题,如果遇到了,查查文档就知道了。在 TransactionTooLargeException 的文档中,其实已经将触发原因详细说明了。

由此得知,通过 Intent 在页面间传递数据是有大小限制的。简单来说,Intent 传输数据的机制中,用到了 Binder。Intent 中的数据,会作为 Parcel 被存储在 Binder 的事务缓冲区(Binder transaction buffer)中的对象进行传输。而这个 Binder 事务缓冲区具有一个有限的固定大小,当前为 1MB。别以为传递 1MB 以下的数据就安全了,这里的 1MB 空间并不是当前操作独享的,而是由当前进程所共享。也就是说 Intent 在 Activity 间传输数据,本身也不适合传递太大的数据。

思考一下

Bundle 的问题?

这里再补充一些细节,Intent 使用 Bundle 存储数据,到底是值传递(深拷贝)还是引用传递?Intent 传输的数据,都存放在一个 Bundle 类型的对象 mExtras 中,Bundle 要求所有存储的数据,都是可被序列化的。在 Android 中,序列化数据需要实现 Serializable 或者 Parcelable。对于基础数据类型的包装类,本身就是实现了 Serializable,而我们自定义的对象,按需实现这两个序列化接口的其中一个即可。

那是不是只要通过 Bundle 传递数据,就会面临序列化的问题?

并不是,Activity 之间传递数据,首先要考虑跨进程的问题,而 Android 中又是通过 Binder 机制来解决跨进程通信的问题。涉及到跨进程,对于复杂数据就要涉及到序列化和反序列化的过程,这就注定是一次值传递(深拷贝)的过程。这个问题用反证法也可以解释,如果是引用传递,那传递过去的只是对象的引用,指向了对象的存储地址,就只相当于一个 Int 的大小,也就根本不会出现 TransactionTooLargeException 异常。

传输数据序列化和 Bundle 没有关系,只与 Binder 的跨进程通信有关。为什么要强调这个呢?

在 Android 中,使用 Bundle 传输数据,并非 Intent 独有的。例如使用弹窗时,DialogFragment 中也可以通过 setArguments(Bundle) 传递一个 Bundle 对象给对话框。
Fragment 本身是不涉及跨进程的,这里虽然使用了 Bundle 传输数据,但是并没有通过 Binder,也就是不存在序列化和反序列化。和 Fragment 数据传递相关的 Bundle,其实传递的是原对象的引用。

有兴趣可以做个试验,弹出 Dialog 时传递一个对象,Dialog 中修改数据后,在 Activity 中检查数据是否被修改了。

分析

接下来就来分析下为什么页面数据传输会有这个量的限制以及这个限制的大小具体是多少。

直接点startActivity()方法进去看

public void startActivity(@SuppressLint(</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小山研磨代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值