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.mzph.cn/news/745503.shtml

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

相关文章

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:XComponent)

可用于EGL/OpenGLES和媒体数据写入,并显示在XComponent组件。 说明: 该组件从API Version 8 开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 构造参数type为"surface"时不支持。 从API version …

VsCode 使用密钥连接 Centos

在 centos 下生成密钥 ssh-keygen 执行上述命令后,一路回车,直到出现如下界面: 查看密钥生成情况 cd /root/.ssh ls 结果如下所示: 服务器上安装公钥 cd /root/.ssh cat id_rsa.pub >> authorized_keys ls >查看确…

Covalent Network(CQT)与 Celo 集成,推动 Web3 下一代现实资产解决方案的发展

Covalent Network(CQT)是一个统一的区块链 API 提供商,其已正式与 Celo 集成,Celo 是一个以移动优先的 EVM 兼容链。这一重要的里程碑旨在提升 Celo 生态系统中开发者的能力,通过授予他们访问关键链上数据的权限&#…

Python | Bootstrap图介绍

在进入Bootstrap 图之前,让我们先了解一下Bootstrap(或Bootstrap 抽样)是什么。 Bootstrap 抽样(Bootstrap Sampling):这是一种方法,我们从一个数据集中重复地取一个样本数据来估计一个总体参数…

Qt教程 — 3.1 深入了解Qt 控件:Buttons按钮

目录 1 Buttons按钮简介 1.1 Buttons按钮简介 1.2 Buttons按钮如何选择 2 如何使用Buttons按钮 2.1 QPushButton使用-如何自定义皮肤 2.2 QToolButton使用-如何设置帮助文档 2.3 QRadioButton使用-如何设置开关效果 2.4 QRadioButton使用-如何设置三态选择框 2.5 QCom…

学习使用postman软件上传文件发起api接口请求

学习使用postman软件上传文件发起api接口请求 设置headers头信息设置body 设置headers头信息 如图设置: KEY:Content-Type VALUE:multipart/form-data 设置body 设置需要上传的key对应的类型为File,上传类型 设置需要上传的文件…

留学生课设|R语言|研究方法课设

目录 INSTRUCTIONS Question 1. Understanding Quantitative Research Question 2. Inputting data into Jamovi and creating variables (using the dataset) Question 3. Outliers Question 4. Tests for mean difference Question 5. Correlation Analysis INSTRUCTIO…

如何安装ES

Elasticsearch入门安装 ES的官方地址:Elasticsearch 平台 — 大规模查找实时答案 | Elastic 我们进到网页可以看到platform(平台) 我们可以看到Elasticsearch logstash kibanba beats 这几个产品 Elasticsearch:分布式&…

某夕夕商品数据抓取逆向之webpack扣取

逆向网址 aHR0cHM6Ly93d3cucGluZHVvZHVvLmNvbQ 逆向链接 aHR0cHM6Ly93d3cucGluZHVvZHVvLmNvbS9ob21lL2JveXNoaXJ0 逆向接口 aHR0cHM6Ly9hcGl2Mi5waW5kdW9kdW8uY29tL2FwaS9naW5kZXgvdGYvcXVlcnlfdGZfZ29vZHNfaW5mbw 逆向过程 请求方式:GET 参数构成 【anti_content】…

基于Transformer的经典目标检测之DETR

背景 DETR,即DEtection TRansformer,是由尼古拉斯卡里翁及其团队于2020年在Facebook AI Research首次提出的,它在目标检测领域开创了一种新的波潮。 虽然目前并未保持最先进(State Of The Art)的地位,但DET…

vr虚拟现实游戏世界介绍|数字文化展览|VR元宇宙文旅

虚拟现实(VR)游戏世界是一种通过虚拟现实技术创建的沉浸式游戏体验,玩家可以穿上VR头显,仿佛置身于游戏中的虚拟世界中。这种技术让玩家能够全方位、身临其境地体验游戏,与游戏中的环境、角色和物体互动。 在虚拟现实游…

IP在网络通信中的重要作用

IP,全称Internet Protocol,即网际互连协议,是TCP/IP体系中的网络层协议。IP作为整个TCP/IP协议族的核心,是构成互联网的基础。它的作用重大且深远,下面将详细阐述IP的定义及其在网络通信中的重要作用。 首先&#xff0…

SpringBoot中的配置文件优先级、bootstrap和application的区别

SpringBoot中的配置文件优先级 SpringBoot项目当中支持的三类配置文件: application.properties application.yml application.yaml 在SpringBoot项目当中,我们要想配置一个属性,可以通过这三种方式当中的任意一种来配置都可以&#xff0…

Elastic Agent 的安装及使用

概述 Elastic Agent是Elastic Stack中的一个全新组件,旨在简化和统一监控和集成管理流程。它是一个轻量级的代理,可以部署到各种不同类型的主机和容器中,用于收集系统指标、日志和事件数据,并将其发送到Elasticsearch进行存储和分…

SpringBoot(自定义转换器+处理Json+内容协商)

文章目录 1.自定义转换器1.代码实例1.save.html2.编写两个bean1.Car.java2.Monster.java 3.WebConfig.java 配置类来自定义转换器4.测试 2.注意事项和细节1.debug查看转换器总数1.打断点2.执行到断点后,选左边的1923.可以看出,加上自定义的转换器一共125…

分类预测 | Matlab实现GSWOA-KELM混合策略改进的鲸鱼优化算法优化核极限学习机的数据分类预测

分类预测 | Matlab实现GSWOA-KELM混合策略改进的鲸鱼优化算法优化核极限学习机的数据分类预测 目录 分类预测 | Matlab实现GSWOA-KELM混合策略改进的鲸鱼优化算法优化核极限学习机的数据分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 GSWOA-KELM分类&#xff0…

react04- mvc 、 mvvm

MVC与MVVM stackoverflow论坛网站 react前端框架 使用框架前: 操作dom > js获取dom元素,事件侦听,修改数据,设置样式。。。 操作dom问题: 直接操作dom,会造成大量的回流、重绘,消耗大量性能操作起来也…

论文阅读——RingMo

RingMo: A Remote Sensing Foundation Model With Masked Image Modeling 与自然场景相比,RS图像存在以下困难。 1)分辨率和方位范围大:受遥感传感器的影响,图像具有多种空间分辨率。此外,与自然图像的实例通常由于重…

尚硅谷SpringBoot3笔记 (二) Web开发

Servlet,SpringMVC视频推荐:53_尚硅谷_servlet3.0-简介&测试_哔哩哔哩_bilibili HttpServlet 是Java Servlet API 的一个抽象类,用于处理来自客户端的HTTP请求并生成HTTP响应。开发人员可以通过继承HttpServlet类并重写其中的doGet()、do…

【图论】计算图的n-hop邻居个数,并绘制频率分布直方图

计算图的n-hop邻居个数,并绘制频率分布直方图 在图论中,n-hop邻居(或称为K-hop邻居)是指从某个顶点出发,通过最短路径(即最少的边数)可以到达的所有顶点的集合,其中n(或…