Android开发Android

解决android.os.TransactionTooLarge

2018-07-09  本文已影响55人  一方乌鸦

做安卓开发的时候,有的时候会遇见如下的crash log:

    07-05 13:34:55.577 6523-6523/com.fooww.soft.android.Presentation W/System.err: java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 2056292 bytes
        at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3998)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:159)

这个bug光看log,大概能猜到原因,但其实很难定位到具体是哪里的问题,尤其是一个Activity包含许多个Fragment。
先说说出现的原因,当你在Activity中的Fragment中,你在使用onSaveInstanceState()方法的时候,存入了过大的数据,导致Activity在数据恢复的时候,抛出异常。

任何时候在你的一个Activity中遇见 TransactionTooLargeException崩溃异常,都意味着这个Activity为了数据恢复而保存的数据,但是一个或多个Bundle发送的数据过大。一次事务所发送的数据是有大小限制的,约1MB。

但是有的时候因为业务逻辑复杂的时候,保存的数据有许多,到底是哪些数据超过了限制呢?为了定位这个问题,我们可以使用下面这个工具来进行定位:

Toolargetool

在你的Activity中进行操作,就会在 logcat 中出现日志,例如

07-05 13:34:47.898 6523-6523/com.fooww.soft.android.Presentation D/TooLargeTool: Fragment.onSaveInstanceState wrote: Bundle@227937474 contains 4 keys and measures 761.0 KB when serialized as a Parcel
    * CLICKPOSITION = 0.0 KB
    * android:view_state = 0.7 KB
    * android:user_visible_hint = 0.1 KB
    * LSTIHITEMS = 760.2 KB
    * fragment arguments = Bundle@211783351 contains 3 keys and measures 1.2 KB when serialized as a Parcel
    * HouseType = 0.2 KB
    * HasHandled = 0.0 KB
    * ConditionSecond = 0.9 KB
07-05 13:34:52.908 6523-6523/com.fooww.soft.android.Presentation D/TooLargeTool: Fragment.onSaveInstanceState wrote: Bundle@13810387 contains 3 keys and measures 1,289.8 KB when serialized as a Parcel
    * CLICKPOSITION = 0.0 KB
    * android:view_state = 0.7 KB
    * LSTIHITEMS = 1,289.1 KB
    * fragment arguments = Bundle@183874957 contains 3 keys and measures 1.2 KB when serialized as a Parcel
    * HouseType = 0.2 KB
    * ConditionRent = 0.9 KB
    * HasHandled = 0.0 KB

这个工具简直是神器有没有,可以看到所有保存到Bundle中的数据及其占用内存大小,很容易的我们发现LSTIHITEMS这个对象包含了 1,289.1 KB的数据,超过了1MB导致后来的TransactionTooLargeException

知道了问题出现的原因,那我们就要接着说说解决办法。其实所有的办法总结起来就是不把过大的数据放进Bundle。除了Bundle,存哪都行。

1.如果Activity中使用了FragmentStatePagerAdapter,换成FragmentPagerAdapter,或者复写saveState()。原因是因为
FragmentStatePagerAdaptersaveState()方法,会对每一个生成的Fragment都保存其Bundle数据,一旦Fragment创建次数过大,比如说...创建了100次,即使Fragment被销毁了,Bundle数据还是被保存下来了, 那么所有的Bundle数据合起来也是很大的。

@Override
public Parcelable saveState() {
    Bundle state = null;
    if (mSavedState.size() > 0) {
        state = new Bundle();
        Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
        mSavedState.toArray(fss);
        state.putParcelableArray("states", fss);
    }
    for (int i=0; i<mFragments.size(); i++) {
        Fragment f = mFragments.get(i);
        if (f != null && f.isAdded()) {
            if (state == null) {
                state = new Bundle();
            }
            String key = "f" + i;
            mFragmentManager.putFragment(state, key, f);
        }
    }
    return state;
} 

2.将要保存的数据存成静态变量。
3.持久化...持久化的时候可以使用这个工具:

Bridge

4.将targetSdkVersion 降到 23及以下,此时数据超了1MB,据说只会有warning,不会crash,这个我没试过...大家酌情考虑。

上一篇下一篇

猜你喜欢

热点阅读