解决android.os.TransactionTooLarge
做安卓开发的时候,有的时候会遇见如下的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。
但是有的时候因为业务逻辑复杂的时候,保存的数据有许多,到底是哪些数据超过了限制呢?为了定位这个问题,我们可以使用下面这个工具来进行定位:
在你的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()
。原因是因为
FragmentStatePagerAdapter
的saveState()
方法,会对每一个生成的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.持久化...持久化的时候可以使用这个工具:
4.将targetSdkVersion 降到 23及以下,此时数据超了1MB,据说只会有warning,不会crash,这个我没试过...大家酌情考虑。