Android 导航组件 - 在整个应用生命周期中存储/访问数据

问题描述

在 Android 中,我们可以使用 Singleton 模式来存储我们在整个应用生命周期中跨多个 Activity 所需的数据。这被认为是内存泄漏和 has its cons,但实际上,这种方法被广泛使用(至少在 Android 编程中)。

说到重点,在 Navigation Component 中,我们遵循在整个应用生命周期中保持可用的单一活动模式。此外,this documentation 表示共享活动级别 viewmodel 可用于在不同片段之间共享数据。我认为这是在多个片段之间共享数据的一种相当合理的方式,但我仍然想在继续之前验证这种方法并将其与 Navigation Component 一起使用。我的问题是我们是否应该使用活动级别 viewmodel 在基于 Navigation Component 的应用程序中存储我在整个应用生命周期中需要的少量数据(请记住我只有一个活动)?此外,与基于单例的方法相比,这种方法有什么影响/优点/缺点?

解决方法

如果您打算为您的应用迁移到单一活动架构,您肯定可以通过跨片段使用相同的视图模型来共享数据。为此,您可以使用 fragment-ktx 工件中的 Kotlin 属性委托。

假设您喜欢在 master 和 detail 片段之间共享相同的数据,您唯一需要做的就是在具有相同活动范围的两个片段中声明两个视图模型实例:

class MasterFragment : Fragment() {

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View,savedInstanceState: Bundle?) {
        super.onViewCreated(view,savedInstanceState)
        model.state.observe(viewLifecycleOwner,{
          // do something with your state
        })
    }
}

class DetailFragment : Fragment() {

    // same scope of your MasterFragment
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View,{
          // do something with your state
        })
    }
}

优点:

  • 生命周期意识:与基于单例的方法不同,您不会冒意外内存泄漏的风险,因为视图模型实例遵循父活动生命周期,因此您无需以编程方式将它们的实例设置为 null当你的碎片被摧毁时。简而言之,您不会冒险保留对无效上下文的引用
  • 无紧密耦合:使用单例方法,您可能会使片段依赖于在您的活动中定义的视图模型实例。这不利于可测试性,并且违反了单一职责原则

此外,建议将单例模式用于“应用程序级”实体,例如数据库或硬件资源,我绝对不建议将单例用于与 UI 相关的事物,例如视图模型类.