Android 开发避坑经验第四篇:正确处理Activity和Fragment的状态保存与恢复

在 Android 开发中,​​Activity​​ 和 ​​Fragment​​ 的状态保存与恢复是一个常见的坑点。如果处理不当,可能会导致应用在屏幕旋转、后台恢复等场景下出现数据丢失、UI 状态不一致等问题。本篇文章将详细探讨如何正确保存和恢复 ​​Activity​​ 与 ​​Fragment​​ 的状态,并提供最佳实践的代码示例。

1. 坑点:Activity 和 Fragment 的重建导致数据丢失

当用户旋转屏幕或者系统回收内存时,​​Activity​​ 或 ​​Fragment​​ 可能会被销毁并重建。如果没有妥善处理状态保存,这将导致用户输入或显示的数据丢失,影响用户体验。

避坑建议:
  • 使用 ​​onSaveInstanceState()​​ 方法来保存 ​​Activity​​ 或 ​​Fragment​​ 的状态。
  • 避免通过构造函数传递数据,改用 ​​Bundle​​ 或 ​​ViewModel​​ 来保存和恢复状态。
  • 对于重要的数据(如用户输入、临时状态等),应保存到 ​​Bundle​​ 中。
示例代码:
class MainActivity : AppCompatActivity() {
    private var userInput: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 恢复状态
        userInput = savedInstanceState?.getString("user_input")
        findViewById<TextView>(R.id.textView).text = userInput
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        // 保存状态
        outState.putString("user_input", userInput)
    }
}

解释: 在 ​​onCreate()​​ 中,通过 ​​savedInstanceState​​ 检查是否有已保存的状态,确保在重建 ​​Activity​​ 后可以正确恢复用户的输入状态。通过 ​​onSaveInstanceState()​​,我们可以在 ​​Activity​​ 被销毁前保存数据,避免用户输入的内容丢失。

2. 坑点:Fragment 状态保存不当导致数据丢失

与 ​​Activity​​ 类似,​​Fragment​​ 的状态保存和恢复也需要通过 ​​onSaveInstanceState()​​ 方法。但由于 ​​Fragment​​ 可能会与 ​​Activity​​ 共享数据,状态的保存和恢复过程可能更加复杂。

避坑建议:
  • 使用 ​​Fragment​​ 的 ​​setArguments()​​ 方法传递初始数据,而不是直接通过构造函数传递。
  • 在 ​​onSaveInstanceState()​​ 中保存 ​​Fragment​​ 的重要状态,并在 ​​onCreateView()​​ 或 ​​onViewCreated()​​ 中恢复这些状态。
示例代码:
class MyFragment : Fragment() {
    private var data: String? = null

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_layout, container, false)
        
        // 恢复状态
        data = savedInstanceState?.getString("saved_data")
        view.findViewById<TextView>(R.id.textView).text = data

        return view
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        // 保存状态
        outState.putString("saved_data", data)
    }

    companion object {
        fun newInstance(data: String): MyFragment {
            val fragment = MyFragment()
            val args = Bundle()
            args.putString("initial_data", data)
            fragment.arguments = args
            return fragment
        }
    }
}

解释: 通过 ​​onSaveInstanceState()​​ 保存 ​​Fragment​​ 的状态,并在 ​​onCreateView()​​ 中恢复数据,可以确保 ​​Fragment​​ 在配置变化或重建时能够保持正确的状态。​​newInstance()​​ 方法通过 ​​Bundle​​ 传递数据,避免了直接通过构造函数传递数据的潜在问题。

3. 坑点:ViewModel 的使用不当导致内存泄漏

​ViewModel​​ 是 Android Jetpack 提供的一个强大工具,专门用于管理 UI 相关的数据。它能够在配置变化(如屏幕旋转)时保留数据。但如果使用不当,可能会导致内存泄漏,特别是在 ​​ViewModel​​ 中持有 ​​Context​​ 或 UI 元素的引用时。

避坑建议:
  • 避免在 ​​ViewModel​​ 中持有 ​​Context​​、​​Activity​​ 或 ​​Fragment​​ 的直接引用。
  • 如果需要使用 ​​Context​​,使用 ​​AndroidViewModel​​ 或通过 ​​Application Context​​ 获取全局上下文。
  • 当 ​​Activity​​ 或 ​​Fragment​​ 销毁时,确保及时清理与 ​​ViewModel​​ 的引用。
示例代码:
class MyViewModel(application: Application) : AndroidViewModel(application) {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String> get() = _data

    fun loadData() {
        // 模拟从全局上下文中加载数据
        val context = getApplication<Application>().applicationContext
        _data.value = "Loaded data from context"
    }
}

解释: ​​AndroidViewModel​​ 提供了一个安全的方式来获取 ​​Application Context​​,避免了直接持有 ​​Activity​​ 或 ​​Fragment​​ 的引用,从而防止内存泄漏。

4. 坑点:复杂对象的状态保存

​Bundle​​ 的大小是有限的,存储过大的数据对象可能会导致 ​​TransactionTooLargeException​​,从而导致应用崩溃。这是开发者在保存复杂对象时常遇到的问题。

避坑建议:
  • 避免将过大的对象(如图像、大型数据列表)直接存入 ​​Bundle​​。
  • 对于大型数据,可以考虑使用 ​​ViewModel​​ 或者 ​​onRetainCustomNonConfigurationInstance()​​ 来保存数据。
  • 对于无法序列化的对象(如自定义类),应通过数据库或文件系统保存,避免将其存入 ​​Bundle​​。
示例代码:
class MyActivity : AppCompatActivity() {
    private lateinit var largeData: List<MyData>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 从ViewModel或其他持久化存储中恢复数据
        val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
        largeData = viewModel.getLargeData()
    }
}

class MyViewModel : ViewModel() {
    private var largeData: List<MyData>? = null

    fun getLargeData(): List<MyData> {
        if (largeData == null) {
            // 模拟从数据源加载数据
            largeData = loadDataFromDatabase()
        }
        return largeData!!
    }
}

解释: 在该代码中,通过 ​​ViewModel​​ 来保存和恢复大型数据,避免了将大型数据直接存入 ​​Bundle​​ 导致的崩溃风险。​​ViewModel​​ 的生命周期与 ​​Activity​​ 和 ​​Fragment​​ 绑定,适合处理这种场景。

结论

在 Android 开发中,正确处理 ​​Activity​​ 和 ​​Fragment​​ 的状态保存与恢复是保证应用稳定性和用户体验的关键。通过使用 ​​onSaveInstanceState()​​、​​ViewModel​​ 等技术,开发者可以避免常见的状态丢失、内存泄漏以及性能问题。这篇文章希望能帮助开发者在实际项目中更加顺利地应对这些坑点,提升应用质量。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

发送成绩的app或小程序推荐

老师们&#xff0c;新学期的第一次月考马上开始&#xff0c;是不是还在为如何高效、便捷地发布成绩而头疼呢&#xff1f;别担心&#xff0c;都2024年了&#xff0c;我们有更智能的方式来解决这个问题&#xff01; 给大家安利一个超级实用的工具——易查分小程序。这个小程序简…

element ui form 表单出现英文提示的解决方案

场景再现&#xff1a; 在使用 form 表单的时候&#xff0c;一般都需要对表单元素进行验证&#xff0c;错误就出现在了这里&#xff0c;除了配置的错误信息&#xff0c;还会出现一个 英文校验提示&#xff0c;如下图&#xff1a; 解决方案 出现的原因是在el-form-item中使用…

把设计模式用起来(3)用不好的原因之时机不对

上一篇&#xff1a;《把设计模式用起来&#xff08;3&#xff09;——用不好的原因 之 实践不足》https://blog.csdn.net/nanyu/article/details/141939342 本篇继续讲设计模式用不好的常见原因&#xff0c;这是第二个&#xff1a;使用设计模式的时机不对。 二、时机不对 这里…

望繁信科技与华恒生物正式签约,共同开启流程数字化转型新篇章

近日&#xff0c;上海望繁信科技有限公司&#xff08;简称“望繁信科技”&#xff09;与安徽华恒生物科技股份有限公司&#xff08;简称“华恒生物”&#xff09;成功举行了战略合作签约仪式。作为全球领先的合成生物制造企业&#xff0c;华恒生物将引入望繁信科技的流程智能管…

3分钟带你了解什么是数据目录

什么是数据目录&#xff1f; 数据目录&#xff0c;顾名思义就是“数据的目录”。这里的“数据”指的是元数据。数据目录通过管理这些元数据&#xff0c;形成一个可用的数据清单&#xff0c;使数据开发者、数据分析师等人员能够通过查阅和搜索等操作&#xff0c;快速找到所需的数…

4052A/4052B/4052C/4052D/4052E/4052F/4052G /4052H信号/频谱分析仪

4052A/4052B/4052C/4052D/4052E/4052F/4052G /4052H信号/频谱分析仪 苏州新利通 Ceyear 4052具备出色的测试动态范围、相位噪声、幅度精度和测试速度&#xff0c;具备频谱分析、I/Q分析、实时频谱分析、瞬态分析、矢量信号分析、脉冲分析、音频分析等丰富的测试功能。 Ceyear…

长沙自闭症寄宿学校推荐,为孩子开启光明未来

在长沙这座历史悠久而又充满活力的城市中&#xff0c;自闭症儿童的成长与教育问题牵动着无数家庭的心。家长们渴望为孩子找到一所能够提供专业康复、温馨关怀与全面教育的学校&#xff0c;为他们的未来铺设一条光明之路。虽然本文起始于长沙的期盼&#xff0c;但我们的目光已跨…

SpringSecurity原理解析(二):认证流程

1、SpringSecurity认证流程包含哪几个子流程&#xff1f; 1&#xff09;账号验证 2&#xff09;密码验证 3&#xff09;记住我—>Cookie记录 4&#xff09;登录成功—>页面跳转 2、UsernamePasswordAuthenticationFilter 在SpringSecurity中处理认证逻辑是在UsernamePas…

Windows10 如何配置python IDE

Windows10 如何配置python IDE 前言Python直接安装&#xff08;快速上手&#xff09;Step1.找到网址Step2.选择版本&#xff08;非常重要&#xff09;Step3. 安装过程Step4. python测试 Anaconda安装&#xff08;推荐&#xff0c;集成了Spyder和Pycharm的安装&#xff09;Step1…

使用功率分析仪测量和分析电抗器(电感器)的方法

高频电抗器用于电动汽车 (EV) 和混合动力汽车 (HEV) 的各种位置。例如&#xff0c;电池和逆变器之间的升压 DC/DC 转换器以及电池充电电路中的 AC/DC 转换器。为了提高整个系统的效率&#xff0c;必须提高每个组成电路的效率&#xff0c;而电抗器是造成这些电路大量损耗的元件之…

Unity 之 【Android Unity FBO渲染】之 [Unity 渲染 Android 端播放的视频] 的一种方法简单整理

Unity 之 【Android Unity FBO渲染】之 [Unity 渲染 Android 端播放的视频] 的一种方法简单整理 目录 Unity 之 【Android Unity FBO渲染】之 [Unity 渲染 Android 端播放的视频] 的一种方法简单整理 一、简单介绍 二、FBO 简单介绍 三、案例实现原理 四、注意事项 五、简…

03 Flask-添加配置信息

回顾之前学习的内容 02 Flask-快速上手 Flask 中最简单的web应用组成 1. 导入核心库 Flask from flask import Flask2. 实例化 web应用 注意&#xff1a;不要漏了 app Flask(__name__) 中的 __name__ 表示&#xff1a;是从当前的py文件实例化 app Flask(__name__)3. 创…

力扣每日一题:1372.二叉树中的最长交错路径

题目 给你一棵以 root 为根的二叉树&#xff0c;二叉树中的交错路径定义如下&#xff1a; 选择二叉树中 任意 节点和一个方向&#xff08;左或者右&#xff09;。如果前进方向为右&#xff0c;那么移动到当前节点的的右子节点&#xff0c;否则移动到它的左子节点。改变前进方…

力扣213-打家劫舍 II(Java详细题解)

题目链接&#xff1a;213. 打家劫舍 II - 力扣&#xff08;LeetCode&#xff09; 前情提要&#xff1a; 本体是打家劫舍的一个变形题&#xff0c;希望大家能先做198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09;&#xff0c;并看一下我上题的讲解力扣198-打家劫舍&…

制证书、制电子印章、签章 -- 演示程序说明

ofd签章系统涉及证书的制作、电子印章制作、签章、验章等环节。关于ofd签章原理&#xff0c;本人写过多篇文章进行了阐述; 见文章《ofd板式文件 电子签章实现方法》、《一款简单易用的印章设计工具》、《签章那些事 -- 让你全面了解签章的流程》。 为了进一步加深对签章过程的理…

RK3229 ADNROID9 hdmi与耳机口同出声音

声卡0怎么配置才能跟HDMI同时输出一样的声音&#xff0c;下面是具体描述&#xff1a; 1、硬件连接 声卡0的连接是芯片的ADC音频输出脚直接接到DA芯片输出 2、cat /proc/asound/cards 0 [rockchiprk3229 ]: rockchip_rk3229 - rockchip,rk3229 rockchip,rk3229 1 [rockchiphdmi …

MFC工控项目实例之十一板卡测试信号输入界面

承接专栏《MFC工控项目实例之十添加系统测试对话框》 相关代码 1、在BoardTest.h文件中添加代码 class CBoardTest : public CDialog { // Construction public:CBoardTest(CWnd* pParent NULL); // standard constructorCButtonST m_btnStart[16];CWinThread* pThread…

FAT32文件系统详细分析 (格式化SD nandSD卡)

FAT32 文件系统详细分析 (格式化 SD nand/SD 卡) 目录 FAT32 文件系统详细分析 (格式化 SD nand/SD 卡)1. 前言2.格式化 SD nand/SD 卡3.FAT32 文件系统分析3.1 保留区分析3.1.1 BPB(BIOS Parameter Block) 及 BS 区分析3.1.2 FSInfo 结构扇区分析3.1.3 引导扇区剩余扇区3.1.4 …

RocketMQ 基础入门

文章内容是学习过程中的知识总结&#xff0c;如有纰漏&#xff0c;欢迎指正 文章目录 前言 RocketMQ 特点 RocketMQ 优势 1. RocketMQ 基本概念 1.1 NameServer 1.1.1 NameServer作用 1.1.2 和zk的区别 1.1.3 高可用保障 1.2 Broker 1.2.1 部署方式 1.2.1.1 单 Master 1.2.1.2 …

C语言 | Leetcode C语言题解之第396题旋转函数

题目&#xff1a; 题解&#xff1a; #define MAX(a, b) ((a) > (b) ? (a) : (b))int maxRotateFunction(int* nums, int numsSize){int f 0, numSum 0;for (int i 0; i < numsSize; i) {f i * nums[i];numSum nums[i];}int res f;for (int i numsSize - 1; i &g…