假如你未对自己的app进行过处理,那么线上各种偶发不可思议的闪退、白屏、数据丢掉,请检查一下是否因此而引发的。
起因
反常重建指的是非配置变更状况下导致的 Activity 从头创立。
常见场景大多是由于内存等资源缺乏,然后导致后台运用被系统收回 ,当咱们切换到前台时,然后触发的重建,这个机制在Android中为 Low Memory Killer
机制,简称 LMK
。
引发问题
- 静态变量丢掉、大局数据、单例丢掉
- 第三方静态变量丢掉(oss等)
- 自己保护的Activity栈丢掉
- Activity + fragment结构,Fragment康复反常白屏
- fragment结构函数中直接传值会闪退
- 表单信息、输入信息、操作信息丢掉。
- 大数据康复困难
作用
-
MainActivity.java中声明了一个静态变量
-
在下一个Activity中打印了静态变量的状况
-
正常状况,
public static
,正常;myList.size()==2
-
反常重启后,静态变量丢掉,
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
- 调用View的saveHierarchyState并且存在当前window的View焦点信息
- 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
并未初始化完成。
处理