【Android Jetpack】LiveData

1. 前言

官网地址:https://developer.android.google.cn/topic/libraries/architecture/livedata

其实在使用ViewModelLifeCycle的时候,我们已经看见了LiveData这个组件。在这篇博客中将继续来了解这个组件。其特点:

  • LiveData是一种可观察的数据存储器类
  • 与常规的可观察类不同,LiveData具有生命周期感知能力。其感知能力遵循其他组件(如 ActivityFragmentService)的生命周期;
  • 感知能力可确保LiveData仅更新处于活跃生命周期状态的应用组件观察者;活跃的观察者对象声明周期处于STARTED RESUMED 状态;
  • LiveData只会将更新通知给活跃的观察者,非活跃观察者不会收到更改通知;
  • 当观察者组件的Lifecycle对象的状态变为 DESTROYED 时,便可移除此观察者,不必担心泄露(当 Activity Fragment的生命周期被销毁时,系统会立即退订它们)。

不妨再次看下这个图:
在这里插入图片描述

1.1 关于观察者模式

我们知道观察者模式,在Java中提供了ObservableObserver来定义被观察者和观察者对象。然后对于可观察对象可以设置观察者,来监听其变化。而在AndroidLifecycle包中提供了对应的LifecycleOwnerLifecycleObserver,而在ActivityFragmentService等组件中已经实现了LifecycleOwner接口,我们在使用的时候,只需要实现LifecycleObserver来标识类是一个观察者,以注册观察。在之前的博客:【Android Jetpack】LifeCycle一文中也曾提到过通过:

lifecycle.addObserver(myLocationObserver)

来为lifecycle来设置一个观察者对象。类似的,前面提到了:LiveData也是一种可观察的数据存储器类。故而也可以设置观察对象,比如:

// viewModel.get() 获取的为LiveData对象
viewModel.get().observe(this, object : androidx.lifecycle.Observer<Int>{
    
    
    override fun onChanged(value: Int?) {
    
    
        livedataText.text = viewModel.getValue().toString()
    }
})

2. LiveData 的优势

  • 确保界面符合数据状态;LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。
  • 不会发生内存泄漏;观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
  • 不会因 Activity 停止而导致崩溃;如果观察者的生命周期处于非活跃状态,则它不会接收任何 LiveData 事件。
  • 不再需要手动处理生命周期;
  • 共享资源;可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。

更加通俗来说,LiveData是一个持有ActivityFragment生命周期的数据容器。当数据源发生改变的时候,可以通知观察者。

2.1 案例一:计时器效果

Activity中定义一个文本,在该控件上每秒自动加一操作。且屏蔽设备配置发生改变后不影响其状态。比如布局文件为ConstraintLayout下放置一个居中的TextView

2.1.1 实现方式一:ViewModel+LiveData

  • 因为需要屏蔽设备配置发生改变的影响,故而这里考虑直接使用ViewModel
  • 考虑代码解耦,这里使用LiveData来设置观察者,也即是我们需要在ViewModel中设置数据类型为LiveData

自定义的ViewModel如下:

class MyNumberViewModel : ViewModel() {
    
    
    private var number: MutableLiveData<Int> = MutableLiveData(0)

    fun set(value: Int) {
    
    
        number.value = value
    }

    // 工作线程需要使用Post
    fun post(value: Int) {
    
    
        number.postValue(value)
    }

    fun get(): LiveData<Int> {
    
    
        return number
    }

    fun getValue(): Int{
    
    
        return number.value?:0
    }
}

因为我们需要使用LiveData对象来注册观察者对象,故而这里get()方法返回的是LiveData对象。且因为需要开启定时任务,故而可以在工作线程完成,所以提供了post方法,然后在Activity中进行设置:

class LiveDataActivity : AppCompatActivity() {
    
    

    private lateinit var viewModel: MyNumberViewModel
    private val livedataText: TextView by lazy {
    
     findViewById(R.id.livedata_text)}

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

        // 获取到ViewModel
        viewModel = ViewModelProvider(
            this,
            ViewModelProvider.NewInstanceFactory()
        ).get(MyNumberViewModel::class.java)

        // 为TextView设置数据
        livedataText.text = viewModel.get().toString()

        // 开始定时任务
        startTimer()

        // 注册一个观察者,监听到ViewModel中数据的变化,如果变化执行onChanged()方法
        // viewModel.get() 获取的为LiveData对象
        viewModel.get().observe(this, object : androidx.lifecycle.Observer<Int>{
    
    
            override fun onChanged(value: Int?) {
    
    
                livedataText.text = viewModel.getValue().toString()
            }
        })
    }

    // 定时任务
    private fun startTimer(){
    
    
        Timer().schedule(
            object : TimerTask() {
    
    
                override fun run() {
    
    
                    viewModel.post(viewModel.getValue() + 1)
                }
            },
            1000, 1000,
        )
    }
}

运行即可看见预想的效果。看完上述案例代码之后是否会有疑惑?肯定是有的,因为这里是LiveData的案例,却引入了ViewModel,且将LiveData放入了自定义的ViewModel类中,为什么需要这么做?LiveData对象本身就可以被观察,为什么还需要放置到ViewModel中?
答:其实在刚开始的要求部分我已经做了引导,需要做到屏蔽设备配置发生改变后不影响其状态,而很方便的ViewModel就可以轻松做到这一点。如果LiveData实例与特定的 ActivityFragment 实例并没有分离开,那么在 ActivityFragment 经历onDestory的时候,LiveData中的数据也就没了。

且上述程序存在一个致命Bug,由于每次屏幕旋转都会执行onCreate,故而会开启多个定时任务,故而这里是不符合题意的。当然,可以加上标志处理,这里不再解决。

2.1.2 实现方式二:configChange

当然,上述使用ViewModel只是一种方式,也可以配置清单文件,设置onConfigChange属性,这里来尝试写一下:

<activity
    android:name=".LiveDataActivity"
    android:configChanges="orientation|screenSize"
    android:exported="true">

然后将LiveData直接定义在Activity中:

class LiveDataActivity : AppCompatActivity() {
    
    

    private val livedataText: TextView by lazy {
    
     findViewById(R.id.livedata_text) }
    private val liveData: MutableLiveData<Int> by lazy {
    
     MutableLiveData<Int>(0) }

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

        // 为TextView设置数据
        livedataText.text = liveData.value.toString()

        // 开始定时任务
        startTimer()

        // 注册一个观察者,监听到ViewModel中数据的变化,如果变化执行onChanged()方法
        // viewModel.get() 获取的为LiveData对象
        liveData.observe(this) {
    
    
            livedataText.text = liveData.value.toString()
        }
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
    
    
        super.onConfigurationChanged(newConfig)
        Log.e("TAG", "onConfigurationChanged: ")
    }

    // 定时任务
    private fun startTimer() {
    
    
        Timer().schedule(
            object : TimerTask() {
    
    
                override fun run() {
    
    
                    liveData.postValue((liveData.value ?: 0) + 1)
                    Log.e("TAG", "run: ${
      
      liveData.value ?: 0}", )
                }
            },
            1000, 1000,
        )
    }
}

猜你喜欢

转载自blog.csdn.net/qq_26460841/article/details/124455415