TransactionTooLargeException缘由分析

前两天测试提了一个crash的bug,崩溃栈以下:java

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
TotalMem\AvailMem   : 3796MB\1037MB
Crash Detail        : android

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 moreapp

虽然说一眼就看出是binder通信引起的崩溃,但就如同OOM问题,定性问题容易,但定位问题却不容易。函数

 

下面开始问题分析:oop

1) 分析抛出的缘由或条件,查看源码android_util_binder.cpp,查看系统层在什么状况下会抛出TransactionTooLargeException,下图中可知测试

   在binder驱动处理失败后,若是传输的parcel体积超过200kb,则会抛出TransactionTooLargeException,所以引起该问题的缘由是binder调用大数据

   传输的数据太大致使,问题分析重点应侧重binder数据传输。spa

 

 2) 分析崩溃栈,找出引起该问题的binder调用是ActivityManagerProxy.activityStopped,从中大概推知问题的发生时机在Activity stopped的时候。设计

 3) 网上百度相关的解决方案,关键词是TransactionTooLargeException activityStopped,现象相似的问题、缘由、解决方案以下:3d

  • 问题缘由: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用于大数据量传输,那就会出现问题。

相关文章
相关标签/搜索