如果你未对自己的app进行过处理,那么线上各种偶发莫名其妙的闪退、白屏、数据丢失,请检查一下是否因此而引发的。
起因
异常重建指的是非配置变更情况下导致的 Activity 重新创建。
常见场景大多是因为内存等资源不足,从而导致后台应用被系统回收 ,当我们切换到前台时,从而触发的重建,这个机制在Android中为 Low Memory Killer
机制,简称 LMK
。
引发问题
- 静态变量丢失、全局数据、单例丢失
- 第三方静态变量丢失(oss等)
- 自己维护的Activity栈丢失
- Activity + fragment构造,Fragment恢复异常白屏
- fragment构造函数中直接传值会闪退
- 表单信息、输入信息、操作信息丢失。
- 大数据恢复困难
效果
1.MainActivity.java中声明了一个静态变量
2.在下一个Activity中打印了静态变量的情况
3.正常情况,public static
,正常;myList.size()==2
4.异常重启后,静态变量丢失,myList.size()==0
验证方式
开发者模式 - 开启限制后台进程,将应用切到后台,打开其他应用消耗内存,并切回应用查看情况。
生命周期情况
根据App切到后台时间、内存情况、操作系统可能有所不同。
可能1:application重启、当前栈顶activity重启,并触发异常存取数据方法;
可能2:application重启,并重启welcome页,类似于冷启动
可能3:正常热启动
解决
数据存储与恢复,很多依赖onSaveInstanceState
、onRestoreInstanceState
。这两方法不过多解释。
1.静态变量丢失、全局数据、单例丢失 ★★★★
使用永久化技术存储重要的数据。如sp、mmkv、sqlite
等。
写一个公共的读写变量的方法,如果该静态变量=null
,则先去SharedPreferences
里恢复,然后再读出数据。
2.第三方对象丢失 ★★★
将第三方初始化移动到application中。
一些同学因为上架市场隐私问题,将第三方的初始化移出了application。解决:第一次在同意协议的activity中初始化,然后再sp里存下状态值true。在application里判断这个sp中的状态值,若为true则在application里初始化。
3.自己维护的Activity栈丢失(不完美解决)★
解决:在异常重启时,恢复数据的方法onSaveInstanceState里判断是否异常重启。如果异常重启就将当前activity加入栈。
解决了获取栈顶activity空指针的问题,但是整个栈未恢复。 (尝试解决:异常存数据时,将整个栈 存起来,以便恢复)
结果:可以正常获取栈顶activity,不会闪退
4.Activity + fragment(ViewPager)构造,Fragment恢复异常白屏 ★★★★★
分析:异常重启activity时,会走完整的activity生命周期,故会重新创建fragment。
若未处理,则activity
会存下原先的AFragment(无信息)
并且在异常重启时恢复。而重新走生命周期onCreate
又会让我们在逻辑上再次创建出一个AFragment
,造成出现2个AFragment
对象存在。使用时会造成显示错乱、数据传输错乱等
若使用ViewPager加载fragment,则还会造成白屏的情况。
原因接上面分析,在FragmentPagerAdapter
的instantiateItem
在attach
时,会找mFragmentManager
里的旧的fragment
导致白屏、数据错乱等。
解决方案:
-
1.在activity里new Fragment时,先去FragmentManager里找,有则直接复用(好。但是改动多)
-
2.在异常存数据时,不存fragments信息(改动少,但是耗资源)
-
3.在取的时候,不取fragments(同2)
方案2实现方式:
在BaseActivity中:
存储
恢复
方案2源码依据:
存储
在FragmentActivity
的onSaveInstanceState
里,会将fragment
以key : "android:support:fragments"
存到outState
在FragmentActivity
的父类Activity.java
中,又以“android:fragments”
为key
,存储fragments
恢复
在FragmentActivity
的onCreate
时,取出存储的fragment信息,恢复到mFragmentManager
。
若使用ViewPager(FragmentPagerAdapter)加载fragment,则还会造成白屏的情况。
在FragmentPagerAdapter
的instantiateItem
在attach
时,会找mFragmentManager
里的旧的fragment
导致白屏。
5.在fragment构造函数中直接传值会闪退 ★★
若fragment中无,无参构造函数,则在异常重启后会闪退。(反射方式启动无参构造函数)
故不能直接在fragment的构造函数中传值。原因同上4。
例:
6.View:用户操作数据、输入恢复/不恢复 ★★★★
系统View大部分都覆写过View.onSaveInstanceState()、View.onRestoreInstanceState()
,如EditText
会存下输入信息、光标信息等。具体View需要阅读源码 + 测试后才能得到实际结果。
自定义View需要开发者自己覆写View.onSaveInstanceState()、View.onRestoreInstanceState()
。
但是往往自带的存储恢复不能满足我们的使用。比如:搜索框输入模糊搜索内容,但是异常恢复以后,输入内容是恢复了,但是下发列表数据未请求接口显示正确数据。
解决:
1.setSaveEnabled
指定是否恢复异常状态
2.覆写onSaveInstanceState()/onRestoreInstanceState()自行处理
源码解析
保存状态逻辑:Activity
会遍历布局层次结构,对于遇到的每个视图,它会调用View.saveHierarchyState()
,而View.dispatchSaveInstanceState()
会调用View.onSaveInstanceState()
。如果View
具有id
,则此方法会调用Parcelable
,这会将其状态保存到View.dispatchSaveInstanceState()
对象并将其返回。
Parcelable
使用View的id
将其保存到共享的持久数据中。
恢复状态逻辑:
跟保存一致。由Activity
下发到window
,然后window
遍历视图树,根据mID
依次恢复每个view
状态。id
不能重复,否则会抛异常
存储
- Activity的window为PhoneWindow
2.调用View的saveHierarchyState并且存在当前window的View焦点信息
3.View.java
那么mViewFlags & SAVE_DISABLED_MASK这个flag又是什么呢?
那么如果我想EditText不恢复之前数据,就可以
恢复
7.intent传值与大数据存储/恢复 ★★★★
使用intent传值,异常重启时,intent中的值会自动恢复
但是大数据传值受到Binder
限制,无法使用intent
传值。而onSaveInstanceState()
使用的Bundle
存值,也受到binder
限制
而大数据传值网上有一种方法利用BigBinder
传输。
但是此时存入的大数据在进程A,异常恢复后此App的进程变成了B,直接变跨进程通信。对象难以恢复。
此传值方式会造成闪退,因为异常重启后,bundle?.getBinder(key)的类型变成了BpBinder,不是BigBinder
解决:使用mmkv、sqlite等技术永久化存储,然后再恢复。包括大数据传值
8.坑1:FragmentStatePagerAdapter 与 FragmentPagerAdapter 区别 ★★★
在用ViewPager
加载Fragment
时,有两种Adapter
供选择。而他俩却有区别,有坑。
-
FragmentStatePagerAdapter
:
1. 在有大量Fragment
时使用,在滑动的时候,会回收不用的fragment
,故开销较大。
2. 异常保存状态时,saveState/restoreState
,ViewPager
会调用,并由它自行保存fragments
。 -
解决:
1. 恢复并复用其原先的fragments
2. 在Adapter中重写saveState,不保存fragments
源码
-
FragmentPagerAdapter:
1. 在少量fragment
时使用,不会回收fragment
,内存占用多。
2. 异常保存状态时,不会自己存fragments
,会取fragmentManage
中的fragments
-
解决:看问题4
源码
9.坑2:异常重启与正常启动,supportFragmentManager绑定FragmentPagerAdapter其中有值的时机不一致。 ★
在异常重启时,如果刚绑定view_pager.adapter = fragmentAdapter
就使用自定义的方法fragment.setData()
传值,此时fragment
并未初始化完成。
解决
作者:铁头娃wawa
链接:https://juejin.cn/post/7195837364681277500
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。