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

如果你未对自己的app进行过处理,那么线上各种偶发莫名其妙的闪退、白屏、数据丢失,请检查一下是否因此而引发的。

起因

异常重建指的是非配置变更情况下导致的 Activity 重新创建。

常见场景大多是因为内存等资源不足,从而导致后台应用被系统回收 ,当我们切换到前台时,从而触发的重建,这个机制在Android中为 Low Memory Killer 机制,简称 LMK

引发问题

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

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

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

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

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

验证方式

开发者模式 - 开启限制后台进程,将应用切到后台,打开其他应用消耗内存,并切回应用查看情况。

 

生命周期情况

根据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,不会闪退

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中:

存储

 恢复

方案2源码依据:
存储

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

 

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

恢复

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

若使用ViewPager(FragmentPagerAdapter)加载fragment,则还会造成白屏的情况。

FragmentPagerAdapterinstantiateItemattach时,会找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使用Viewid将其保存到共享的持久数据中。

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

存储
  1. 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/restoreStateViewPager会调用,并由它自行保存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
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/537896.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ClinicalMamba:长距离不垮,超模也望尘莫及;定制化信息提取,个性化精确到点

ClinicalMamba:长距离不垮,超模也望尘莫及;定制化信息提取,个性化精确到点 1. 引言2. 相关工作早期临床语言模型的局限性Clinical BERT和ClinicalBERT等模型的进步GatorTron和NYUTron等大模型的贡献提出长文本处理的需求和之前方法…

C++第四弹---类与对象(一)

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 类与对象 1、面向过程和面向对象初步认识 2、类的引入 3、类的定义 4、类的访问限定符及封装 4.1、访问限定符 4.2、封装 5、类的作用域 6、类的…

ElasticSearch之Nested对象

写在前面 本文看下es的nested嵌套对象相关内容。 1:es用了啥范式? 在关系型数据库中定义了6大数据库范式,即1,2,3,BC,4,5的NF(normal form),分别如下: 1N…

Apache Paimon 的 CDC Ingestion 概述

CDC Ingestion 1)概述 Paimon支持schema evolution将数据插入到Paimon表中,添加的列将实时同步到Paimon表,并且无需重启同步作业。 目前支持的同步方式如下: MySQL Synchronizing Table: 将MySQL中的一个或多个表同步到一个Pa…

Day15 面向对象进阶——接Day14

Day15 面向对象进阶——接Day14 文章目录 Day15 面向对象进阶——接Day14一、访问修饰符二、Object三、深入String的equals()方法四、final 一、访问修饰符 1、含义:修饰类、方法、属性,定义使用的范围 2、经验: 2.1.属性一般使用private修…

Linux第77步_处理Linux并发的相关函数

了解linux中的“原子整形数据”操作、“原子位数据”操作、自旋锁、读写锁、顺序锁、信号量和互斥体,以及相关函数。 并发就是多个“用户”同时访问同一个共享资源。如:多个线程同时要求读写同一个EEPROM芯片,这个EEPROM就是共享资源&#x…

面试六--TCP粘包问题

1.流式传输协议 流式传输协议(Streaming Protocol)是一种用于在网络上传输数据的通信协议,它允许数据以连续的流的形式进行传输,而不是一次性发送完整的数据包。流式传输协议即协议的内容是像流水一样的字节流,内容与内…

【Flutter】报错Target of URI doesn‘t exist ‘package:flutter/material.dart‘

运行别人项目 包无法导入报错:Target of URI doesn’t exist ‘package:flutter/material.dart’ 解决方法 flutter packages get成功 不会报错

BIT-1-深度剖析数据在内存中的存储(C语言进阶)

本章重点 数据类型详细介绍 整形在内存中的存储:原码、反码、补码大小端字节序介绍及判断浮点型在内存中的存储解析 1. 数据类型介绍 前面我们已经学习了基本的内置类型: char //字符数据类型 short //短整型 int //整形 long //长整型…

如何在Windows系统安装Node.js环境并制作html页面发布公网远程访问?

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

代码随想录算法训练营第八天|344. 反转字符串

344. 反转字符串 已解答 简单 相关标签 相关企业 提示 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 示例 1&#…

供应IMX290LQR-C芯片现货

长期供应各品牌芯片现货,SONY索尼SONY索尼CMOS/CCD芯片全系列全新现货优势出: IMX225LQR-C IMX415-AAQR-C IMX290LQR-C imx273llr-C IMX397CLN-C IMX637-AAMJ-C IMX647-AAMJ-C IMX991-A***-C IMX991-AABJ-C IMX287LLR-C IMX287LQR-C IMX297L…