Jetpack 之 ViewModel和LiveData

56 阅读5分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情

ViewModel引入见 Lifecycle官方库

LiveData引入见 Lifecycle官方库

Kotlin 扩展模块支持使用多个 AndroidX 依赖项。这些模块的名称后附加了后缀“-ktx”。例如:

implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"

会变为

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

LiveData简介

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。

使用 LiveData 具有以下优势:

确保界面符合数据状态

LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。这样一来,您无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。

不会发生内存泄漏

观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。 不会因 Activity 停止而导致崩溃 如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。

不再需要手动处理生命周期

界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。

数据始终保持最新状态

如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。

适当的配置更改

如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。

共享资源

您可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。如需了解详情,请参阅扩展 LiveData

使用 LiveData

创建 LiveData 的实例以存储某种类型的数据。这通常在 ViewModel 类中完成。

class MyViewModel:ViewModel() {

       val count = MutableLiveData<Int>()
       //这种写法避免修改
    //private val _count = MutableLiveData<Int>()
    //val count: LiveData<Int>
    //    get() = _count

//在非主线程更新数据
 count.postValue(1)
 //在主线程更新数据
  count.value = 1
}

观察LiveData

class MainActivity : AppCompatActivity(){

 private val myViewModel by viewModels<MyViewModel>()
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
   		myViewModel.count.observe(this, Observer {
            binding.btnRandom.text = it.toString()
        })
     }
   }
    

这样当count的值发生变化的时候就会通知到observe方法。

ViewModel简介

场景一 Activity 中包含用户列表,为配置更改重新创建 Activity 后,新 Activity 必须重新提取用户列表。对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。

场景二 对于需要一些时间才能返回的异步调用。界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。

如果要求Activity也负责从数据库或网络加载数据,那么会使类越发膨胀。为Activity分配过多的责任而不是将工作委托给其他类。Activity分配过多的责任也会大大增加测试的难度。

从Activity中分离出视图数据所有权的操作更容易且更高效。

实现 ViewModel

class MyViewModel:ViewModel() {
  var numbers = 0
}

然后您可以在Activity中调用

class MainActivity : AppCompatActivity(){

 private val myViewModel by viewModels<MyViewModel>()
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        binding.btnAdd.setOnClickListener {
            //viewmodel的数据不跟生命周期走,不管生命周期到了哪一步,viewmodel的数据都存在
            //如果重新创建了该 Activity,它接收的 MyViewModel 实例与第一个 Activity 创建的实例相同。
            // 当所有者 Activity 完成时,框架会调用 ViewModel 对象的 onCleared() 方法,以便它可以清理资源。
            //不要往viewmodel中传入context,会导致内存泄露
            //不要使用context,请使用AndroidViewModel中的Application
            //比如屏幕旋转,viewmodel中的number是不会变化的
            binding.btnAdd.text = (++myViewModel.numbers).toString()

        }
     }
   }
    

ViewModel 的生命周期

在这里插入图片描述 您通常在系统首次调用 Activity 对象的 onCreate() 方法时请求 ViewModel。系统可能会在 activity 的整个生命周期内多次调用 onCreate(),如在旋转设备屏幕时。ViewModel 存在的时间范围是从您首次请求 ViewModel 直到 activity 完成并销毁

在 Fragment 之间共享数据

假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。 那么就需要用activityViewModels来实现数据共享了。

创建ViewModel
class MyViewModel:ViewModel() {

    val count = MutableLiveData<Int>()
    
    //随机赋值
    fun updateCount(){
        count.value = (1..10).random()
    }
    
}
创建AFragment 来修改数据
class AFragment : Fragment() {

    private lateinit var binding :FragmentABinding

    //activityViewModels是通信的关键
    //fragment,activity,其他fragment 三者皆可通信
    private val myViewModel by activityViewModels<MyViewModel>()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        binding = FragmentABinding.inflate(inflater,container,false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        myViewModel.count.observe(viewLifecycleOwner, Observer {
            Log.v("zx","随机$it")
        })
        //数据数据
		binding.btnUpdate.setOnClickListener {
            myViewModel.updateCount()
        }
   }
class BFragment : Fragment() {

    private lateinit var binding: FragmentBBinding
    private val myViewModel by activityViewModels<MyViewModel>()
    
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentBBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        //当A调用updateCount方法后,这里就会收到回调
        myViewModel.count.observe(viewLifecycleOwner, Observer {
            Log.v("zx","随机B$it")
        })

更多: Android之ViewModel的使用