TransactionTooLargeException原因分析
Posted tgltt
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TransactionTooLargeException原因分析相关的知识,希望对你有一定的参考价值。
前两天测试提了一个crash的bug,崩溃栈如下:
Device Manufacturer : Meizu
Device Model : M5 Note
android Version : 7.0
Android SDK : 24
App VersionName : 1.1.2
App VersionCode : 16
App Max Mem : 512MB
UUID : 864105030589222
Timestamp : 10-16 14:27:22.880
CurrentThread : main#1
TotalMemAvailMem : 3796MB1037MB
Crash Detail :
java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 531500 bytes
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:4112)
at android.os.Handler.handleCallback(Handler.java:836)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:203)
at android.app.ActivityThread.main(ActivityThread.java:6519)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1113)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:974)
Caused by: android.os.TransactionTooLargeException: data parcel size 531500 bytes
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Binder.java:627)
at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3990)
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:4104)
... 7 more
虽说一眼就看出是binder通讯引发的崩溃,但就如同OOM问题,定性问题容易,但定位问题却不容易。
下面开始问题分析:
1) 分析抛出的原因或条件,查看源码android_util_binder.cpp,查看系统层在什么情况下会抛出TransactionTooLargeException,下图中可知
在binder驱动处理失败后,如果传输的parcel体积超过200kb,则会抛出TransactionTooLargeException,因此引发该问题的原因是binder调用
传输的数据太大导致,问题分析重点应侧重binder数据传输。
2) 分析崩溃栈,找出引发该问题的binder调用是ActivityManagerProxy.activityStopped,从中大概推知问题的发生时机在Activity stopped的时候。
3) 网上百度相关的解决方案,关键词是TransactionTooLargeException activityStopped,现象类似的问题、原因、解决方案如下:
问题原因:FragmentStatePagerAdapter
的实现有缺陷,因为其默认实现会持续保存历史Fragment实例的状态数据历史,在逐渐地积累、保存数据后,最终导致发送的数据包体积超过限制200KB 。
参见:https://stackoverflow.com/questions/11451393/what-to-do-on-transactiontoolargeexception
- 解决方案:重写
FragmentStatePagerAdapter
的saveState方法,使其不保存历史Fragment实例的状态数据。
4) 工程中崩溃点也恰巧使用了FragmentStatePagerAdapter
,现象也类似,然后立即应用该方案,但没有效果。
5)继续分析该问题,深入代码进行白盒分析,根据日志输出和代码结构,最后定位问题原因是构建Fragment实例时传递ArrayList给Fragment的构造函数,
在Fragment的构造函数内部将该ArrayList作为Parcelable存放在Bundle,以供该Fragment初始化时从bundle中读取, 在数据量比较大时,就会抛出TransactionTooLargeException。
6) 解决方案:构建Fragment实例时传递ArrayList给Fragment的构造函数,在Fragment被加载时无需从Bundle中读取,这样可避免TransactionTooLargeException,又提高程序执行效率。
7) 总结
TransactionTooLargeException经常出现在binder通信的场景中,导致其出现的直接原因是binder通信的数据包过大,而根本原因是使用者的理解和设计软件问题,因为binder通信的设计初衷是
跨进程的小规模数据体量的通信,从其内存设置就可看出:binder空间最大是1MB,而且是被所有进程共享。如果不理解binder的设计和适用场景,错误地将binder用于大数据量传输,那就会出现问题。
以上是关于TransactionTooLargeException原因分析的主要内容,如果未能解决你的问题,请参考以下文章