假如你未对自己的app进行过处理,那么线上各种偶发不可思议的闪退、白屏、数据丢掉,请检查一下是否因此而引发的。

起因

反常重建指的是非配置变更状况下导致的 Activity 从头创立。

常见场景大多是由于内存等资源缺乏,然后导致后台运用被系统收回 ,当咱们切换到前台时,然后触发的重建,这个机制在Android中为 Low Memory Killer 机制,简称 LMK

引发问题

  1. 静态变量丢掉、大局数据、单例丢掉
  2. 第三方静态变量丢掉(oss等)
  3. 自己保护的Activity栈丢掉
  4. Activity + fragment结构,Fragment康复反常白屏
  5. fragment结构函数中直接传值会闪退
  6. 表单信息、输入信息、操作信息丢掉。
  7. 大数据康复困难

作用

  1. MainActivity.java中声明了一个静态变量

    Android 异常重启--踩坑归来--干货篇

    Android 异常重启--踩坑归来--干货篇

  2. 在下一个Activity中打印了静态变量的状况

    Android 异常重启--踩坑归来--干货篇

  3. 正常状况,public static,正常;myList.size()==2

  4. 反常重启后,静态变量丢掉,myList.size()==0

验证办法

开发者模式 – 敞开约束后台进程,将运用切到后台,翻开其他运用消耗内存,并切回运用查看状况。

Android 异常重启--踩坑归来--干货篇

生命周期状况

依据App切到后台时刻、内存状况、操作系统或许有所不同。
或许1:application重启、当前栈顶activity重启,并触发反常存取数据办法;
或许2:application重启,并重启welcome页,类似于冷发动
或许3:正常热发动

处理

数据存储与康复,许多依靠onSaveInstanceStateonRestoreInstanceState。这两办法不过多解说。

1.静态变量丢掉、大局数据、单例丢掉 ★★★★

运用永久化技能存储重要的数据。如sp、mmkv、sqlite等。
写一个公共的读写变量的办法,假如该静态变量=null,则先去SharedPreferences里康复,然后再读出数据。

2.第三方目标丢掉 ★★★

将第三方初始化移动到application中
一些同学由于上架商场隐私问题,将第三方的初始化移出了application。处理:第一次在同意协议的activity中初始化,然后再sp里存下状况值true。在application里判别这个sp中的状况值,若为true则在application里初始化。

3.自己保护的Activity栈丢掉(不完美处理)★

处理:在反常重启时,康复数据的办法onSaveInstanceState里判别是否反常重启。假如反常重启就将当前activity参加栈。
处理了或许栈顶activity空指针的问题,可是整个栈未康复。 (尝试处理:反常存数据时,将整个栈 存起来,以便康复)
成果:可以正常获取栈顶activity,不会闪退

Android 异常重启--踩坑归来--干货篇

4.Activity + fragment(ViewPager)结构,Fragment康复反常白屏 ★★★★★

剖析:反常重启activity时,会走完好的activity生命周期,故会从头创立fragment。
若未处理,则activity会存下原先的AFragment(无信息)并且在反常重启时康复。而从头走生命周期onCreate又会让咱们在逻辑上再次创立出一个AFragment,形成出现2个AFragment目标存在。运用时会形成显示紊乱、数据传输紊乱等

若运用ViewPager加载fragment,则还会形成白屏的状况。
原因接上面剖析,在FragmentPagerAdapterinstantiateItemattach时,会找mFragmentManager里的旧的fragment导致白屏、数据紊乱等

处理方案:

  • 1.在activity里new Fragment时,先去FragmentManager里找,有则直接复用(好。可是改动多)

  • 2.在反常存数据时,不存fragments信息(改动少,可是耗资源)

  • 3.在取的时分,不取fragments(同2)

方案2完成办法:

在BaseActivity中:

存储

Android 异常重启--踩坑归来--干货篇

康复

Android 异常重启--踩坑归来--干货篇

方案2源码依据:

存储

FragmentActivityonSaveInstanceState里,会将fragmentkey : "android:support:fragments"存到outState

Android 异常重启--踩坑归来--干货篇

FragmentActivity的父类Activity.java中,又以“android:fragments”key,存储fragments

Android 异常重启--踩坑归来--干货篇

康复

FragmentActivityonCreate时,取出存储的fragment信息,康复到mFragmentManager

Android 异常重启--踩坑归来--干货篇

Android 异常重启--踩坑归来--干货篇

Android 异常重启--踩坑归来--干货篇

Android 异常重启--踩坑归来--干货篇

若运用ViewPager(FragmentPagerAdapter)加载fragment,则还会形成白屏的状况。

FragmentPagerAdapterinstantiateItemattach时,会找mFragmentManager里的旧的fragment导致白屏

Android 异常重启--踩坑归来--干货篇

5.在fragment结构函数中直接传值会闪退 ★★

若fragment中无,无参结构函数,则在反常重启后会闪退。(反射办法发动无参结构函数)
故不能直接在fragment的结构函数中传值。原因同上4。

例:

Android 异常重启--踩坑归来--干货篇

6.View:用户操作数据、输入康复/不康复 ★★★★

系统View大部分都覆写过View.onSaveInstanceState()、View.onRestoreInstanceState(),如EditText会存下输入信息、光标信息等。详细View需求阅读源码 + 测验后才能得到实践成果。

自定义View需求开发者自己覆写View.onSaveInstanceState()、View.onRestoreInstanceState()

可是往往自带的存储康复不能满足咱们的运用。比如:查找框输入模糊查找内容,可是反常康复以后,输入内容是康复了,可是下发列表数据未恳求接口显示正确数据。

Android 异常重启--踩坑归来--干货篇

处理:
1.setSaveEnabled指定是否康复反常状况

Android 异常重启--踩坑归来--干货篇

Android 异常重启--踩坑归来--干货篇

2.覆写onSaveInstanceState()/onRestoreInstanceState()自行处理

源码解析

保存状况逻辑:
Activity遍历布局层次结构,对于遇到的每个视图,它会调用View.saveHierarchyState(),而View.dispatchSaveInstanceState()会调用View.onSaveInstanceState()。假如View具有id ,则此办法会调用Parcelable,这会将其状况保存到View.dispatchSaveInstanceState()目标并将其回来。

Parcelable运用View的id将其保存到共享的持久数据中。

康复状况逻辑:
跟保存一致。由Activity下发到window,然后window遍历视图树,依据mID依次康复每个view状况。id不能重复,否则会抛反常

存储
  1. Activity的window为PhoneWindow

Android 异常重启--踩坑归来--干货篇

  1. 调用View的saveHierarchyState并且存在当前window的View焦点信息

Android 异常重启--踩坑归来--干货篇

  1. View.java

Android 异常重启--踩坑归来--干货篇

Android 异常重启--踩坑归来--干货篇

那么mViewFlags & SAVE_DISABLED_MASK这个flag又是什么呢?

Android 异常重启--踩坑归来--干货篇

那么假如我想EditText不康复之前数据,就可以

Android 异常重启--踩坑归来--干货篇

康复

Android 异常重启--踩坑归来--干货篇

Android 异常重启--踩坑归来--干货篇

Android 异常重启--踩坑归来--干货篇

7.intent传值与大数据存储/康复 ★★★★

运用intent传值,反常重启时,intent中的值会自动康复

Android 异常重启--踩坑归来--干货篇

可是大数据传值遭到Binder约束,无法运用intent传值。而onSaveInstanceState()运用的Bundle存值,也遭到binder约束

而大数据传值网上有一种办法运用BigBinder传输。
可是此刻存入的大数据在进程A,反常康复后此App的进程变成了B,直接变跨进程通信。目标难以康复。

此传值办法会形成闪退,由于反常重启后,bundle?.getBinder(key)的类型变成了BpBinder,不是BigBinder

Android 异常重启--踩坑归来--干货篇

处理:运用mmkv、sqlite等技能永久化存储,然后再康复。包含大数据传值

8.坑1:FragmentStatePagerAdapter 与 FragmentPagerAdapter 差异 ★★★

在用ViewPager加载Fragment时,有两种Adapter供选择。而他俩却有差异,有坑。

  • FragmentStatePagerAdapter
    1.在有大量Fragment时运用,在滑动的时分,会收回不必的fragment,故开销较大
    2.反常保存状况时,saveState/restoreStateViewPager会调用,并由它自行保存fragments

  • 处理
    1.康复并复用其原先的fragments
    2.在Adapter中重写saveState,不保存fragments

Android 异常重启--踩坑归来--干货篇

源码

Android 异常重启--踩坑归来--干货篇
  • FragmentPagerAdapter:
    1.在少数fragment时运用,不会收回fragment,内存占用多。
    2.反常保存状况时,不会自己存fragments,会取fragmentManage中的fragments

  • 处理:看问题4

源码

Android 异常重启--踩坑归来--干货篇

9.坑2:反常重启与正常发动,supportFragmentManager绑定FragmentPagerAdapter其中有值的机遇不一致。 ★

在反常重启时,假如刚绑定view_pager.adapter = fragmentAdapter就运用自定义的办法fragment.setData()传值,此刻fragment并未初始化完成。

Android 异常重启--踩坑归来--干货篇

处理

Android 异常重启--踩坑归来--干货篇

Android 异常重启--踩坑归来--干货篇