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

news2024/9/25 15:29:23

如果你未对自己的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.coloradmin.cn/o/1517531.html

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

相关文章

python统计分析——单变量分布的特征描述之分布中心

参考资料:python统计分析【托马斯】 也可查看:python统计分析——单变量描述统计-CSDN博客 当我们有一个来自分布的数据样本时,我们可以用不同的参数来描述分布中心。因此,数据可以用两种方式来评估: (1&a…

Xcode15.3 -Library ‘iconv2.4.0‘ not found

今天升级了一下Mac mini 和Xcode15.3,运行项目就报 Library ‘iconv2.4.0’ not found的错误 xcode升级到:15.3(15A240d) 项目在旧版本下,是能通过编译 并且能运行的。 解决方法: 方案1:在Build Phases --> Link…

SSL VPN基础原理

目录 SSL ---安全传输协议(安全套接层)---TLS ----传输层安全协议 SSL的工作原理 SSL会话建立的过程 ​编辑 数据传输过程中的封装示意图 无客户端认证的过程 有客户端认证的过程 SSL VPN的核心技术---虚拟网关技术 服务器验证的点: 资源…

Gitlab光速发起Merge Request

前言 在我们日常开发过程中需要经常使用到Merge Request,在使用过程中我们需要来回在开发工具和UI界面之前来回切换,十分麻烦。那有没有一种办法可以时间直接开发开工具中直接发起Merge Request呢? 答案是有的。 使用 Git 命令方式创建 Me…

npm、nodejs和vue之间关系和区别介绍

本文讲解npm、Node.js和Vue.js这三者之间的关系和区别,以及它们各自的特点。 首先,让我们来了解一下Node.js。 **Node.js** 是一个开源的服务器端运行环境,它允许开发者使用JavaScript来编写服务器端的代码。在传统的Web开发中&#…

免费 Copilot 用户可以访问 OpenAI 的 GPT-4 Turbo;面向 3D 虚拟环境的多面手 AI 代理

🦉 AI新闻 🚀 免费 Copilot 用户可以访问 OpenAI 的 GPT-4 Turbo 摘要:微软宣布免费版Copilot已升级到GPT-4 Turbo模型,所有用户都可以免费使用。此外,Copilot Pro新增了GPT Builder工具,订阅者可创建自定…

网上商城购物系统|基于springboot框架+ Mysql+Java+B/S架构的网上商城购物系统设计与实现(可运行源码+数据库+设计文档+部署说明)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 管理员功能登录前台功能效果图 用户功能模块 系统功能设计 数据库E-R图设计 lunwen参…

【数据结构】哈希表(哈希函数+负载因子+解决冲突方法)

文章目录 五、哈希表1.概念2.哈希函数1.设计哈希函数:2.常见的哈希函数1.直接定址法(常用):2.除留余数法(常用) 3.负载因子4.解决冲突1.闭散列法(开放地址法)1.线性探测法&#xff1…

docker容器启动rabbitmq

docker容器启动rabbitmq 一、RabbitMQ部署1.1.在线拉取mq镜像1.2.运行mq容器1.3.访问mq 二、RabbitMQ的集群2.1.集群分类2.1.设置 /etc/hosts文件 endl 一、RabbitMQ部署 1.1.在线拉取mq镜像 # 在线拉取 docker pull rabbitmq:3-management1.2.运行mq容器 docker run \ -e R…

python爬虫 Appium+mitmdump 京东商品

爬虫系列:http://t.csdnimg.cn/WfCSx 前言 我们知道通过Charles进行抓包可以发现其参数相当复杂,Form 表单有很多加密参数。如果我们只用 Charles 探测到这个接口链接和参数,还是无法直接构造请求的参数,构造的过程涉及一些加密…

Kafka-生产者报错javax.management.InstanceAlreadyExistsException

生产者发送消息到 kafka 中,然后控制台报错 然后根据日志查看 kafka 的源码发现了问题原因 说的是MBean已经注册了,然后报异常了,这样就会导致生产者的kafka注册失败, 原因是项目上生产者没有配置clientId,默认都是空导致的, 多个生产者(项目)注册到kafka集群中的 id 都相同。 …

水泵房远程监控物联网系统

随着物联网技术的快速发展,越来越多的行业开始利用物联网技术实现设备的远程监控与管理。水泵房作为城市供水系统的重要组成部分,其运行状态的监控与管理至关重要。HiWoo Cloud作为专业的物联网云服务平台,为水泵房远程监控提供了高效、稳定、…

SpringCloud(22)之Sentinel实战应用

一、Sentinel核心库 sentinel主页:主页 alibaba/Sentinel Wiki GitHub 1.1 Sentinel介绍 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点&…

有趣的前端知识(三)

推荐阅读 有趣的前端知识(一) 有趣的前端知识(二) 文章目录 推荐阅读JS内置对象JS外部对象BOM模型history对象screen对象navigator对象 DOM(文档对象模型)DOM的方法(对于节点的操作&#xff09…

Rudolf and the Ball Game

传送门 题意 思路 暴力枚举每一个妆台的转换条件 code #include<iostream> #include<cstdio> #include<stack> #include<vector> #include<algorithm> #include<cmath> #include<queue> #include<cstring> #include<ma…

ChatGPT 插件Plugin集合

ChatGPT的插件功能推出一段时间了&#xff0c;陆陆续续的上架了得有200了。 但是其中大部分都不是很好用&#xff0c;并且找起来也复杂。 推荐一个不知名热心人做的导航页。 ChatGPT Plugins Overview 基本上集合了所有的插件&#xff0c;并且还在实时更新中。 需要升级4.0&a…

el-input设置max、min无效的解决方案

目录 一、方式1&#xff1a;type“number” 二、方式2&#xff1a;oninput&#xff08;推荐&#xff09; 三、计算属性 如下表所示&#xff0c;下面为官方关于max&#xff0c;min的介绍&#xff1a; el-input&#xff1a; max原生属性&#xff0c;设置最大值min原生属性&a…

<Senior High School Math>: inequality question

( 1 ) . o m i t (1). omit (1).omit ( 2 ) . ( a 2 − b 2 ) ( x 2 a 2 − y 2 b 2 ) ( x 2 y 2 ) − ( a 2 y 2 b 2 b 2 x 2 a 2 ) ≤ x 2 y 2 − 2 x y ( x − y ) 2 (2). (a^2-b^2)(\frac{x^2}{a^2} - \frac{y^2}{b^2})(x^2y^2)-(\frac{a^2y^2}{b^2}\frac{b^2x^2}{a^…

自然语言处理NLP:tf-idf原理、参数及实战

大家好&#xff0c;tf-idf作为文体特征提取的常用统计方法之一&#xff0c;适合用于文本分类任务&#xff0c;本文将从原理、参数详解和实际处理方面介绍tf-idf&#xff0c;助力tf-idf用于文本数据分类。 1.tf-idf原理 tf 表示词频&#xff0c;即某单词在某文本中的出现次数与…

【刷题】双指针进阶

请看入门篇 &#xff1a;双指针入门 送给我们一句话&#xff1a; 如今我努力奔跑&#xff0c;不过是为了追上那个曾经被寄予厚望的自己 —— 约翰。利文斯顿 双指针进阶 Leetcode 611 有效三角形的个数Leetcode LCR179.查找总价格为目标值的两个商品Leetcode 15.三数之和Thanks…