Android 锁屏/跳转Activity时onSaveInstanceState报TransactionTooLargeException
Activity onSaveInstanceState 环节 TransactionTooLargeException 异常排查
问题场景
- 页面结构:ViewPager挂载4个承载RecyclerView的Fragment,RecyclerView数据源为
HashMap<String, ArrayList<AMedia>>类型的大体积对象 - 现象1:横竖屏切换时,
onSaveInstanceState、onViewStateRestored正常触发,数据传递无异常,不崩溃 - 现象2:锁屏、启动其他Activity时,仅触发
onSaveInstanceState后应用直接崩溃,抛出TransactionTooLargeException,日志显示Parcel数据大小达45MB左右 - 原有状态保存实现代码:
@Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putSerializable("medias", mMapMedias); } @SuppressWarnings("unchecked") @Override public void onViewStateRestored(@Nullable Bundle savedInstanceState) { super.onViewStateRestored(savedInstanceState); if (savedInstanceState != null) { mMapMedias = (HashMap<String, ArrayList<AMedia>>) savedInstanceState.getSerializable("medias"); render(); } }
- 核心崩溃日志片段:
JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 45037652) AndroidRuntime: FATAL EXCEPTION: main java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 45037652 bytes at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:161) Caused by: android.os.TransactionTooLargeException: data parcel size 45037652 bytes at android.os.BinderProxy.transactNative(Native Method) at android.app.IActivityTaskManager$Stub$Proxy.activityStopped(IActivityTaskManager.java:4408)
核心原因:两种场景的数据流转路径差异
横竖屏切换和锁屏/跳转其他Activity时,onSaveInstanceState保存的Bundle传输逻辑完全不同:
- 横竖屏切换属于应用进程内的配置变更:保存的状态Bundle只在当前应用进程内部流转,不会走Binder跨进程通信,不受Binder事务的大小限制,哪怕Bundle体积达到几十MB也能正常传递,因此不会触发崩溃。
- 锁屏、启动其他Activity属于Activity退到后台的场景:系统需要将Bundle中保存的所有状态数据,通过Binder跨进程传给系统服务(ActivityTaskManager)留存,而Android Binder事务的共享缓冲区总大小仅约1MB,单次传输数据远超阈值时就会直接抛出
TransactionTooLargeException。从日志可以看到,45MB的异常数据几乎全部来自Fragment保存的大体积媒体HashMap,View层级、ActivityResult相关状态占比可忽略。
可行解决方案
onSaveInstanceState的设计初衷仅用于保存轻量级UI状态,绝对不能用来存放大体积业务数据,可按以下方式修复:
- 大体积业务数据做独立留存
- 横竖屏等配置变更场景的数据留存,使用
ViewModel存储mMapMedias,ViewModel生命周期天然覆盖配置变更流程,不需要做序列化/反序列化,也不会占用Bundle空间,配置变更后直接从ViewModel取数据渲染即可。 - 页面退后台需要留存数据时,根据数据生命周期选择单例、Room本地数据库、本地文件做存储,不要塞入系统持久化的状态Bundle中。
- 横竖屏等配置变更场景的数据留存,使用
- 精简状态保存内容
onSaveInstanceState中仅保存轻量级UI状态,比如RecyclerView滚动位置、当前选中Tab位置、输入框未提交的短文本等,单Bundle总大小尽量控制在几十KB级别,直接移除原有代码中往Bundle塞大体积Serializable HashMap的逻辑。
- 后台杀死场景恢复适配
- 如果应用在后台被系统杀死后重新返回页面,不要依赖状态Bundle恢复大体积媒体数据,走正常的业务接口拉取/本地缓存读取逻辑重新加载数据即可。
内容的提问来源于stack exchange,提问作者X6Entrepreneur
相关产品推荐
相关产品推荐

