LiveData详解

1,149 阅读3分钟

LiveData 是一种可观察的数据存储器类,与常规的Obserable类不同,LiveData 可感知应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。
可以与Lifecycle配合使用,当相应的 Lifecycle对象的状态变为 DESTROYED 时,便可移除此观察者,避免出现内存泄漏。 Lifecycle详解 ViewModel详解

一:使用LiveData的优势

  • 保持UI与数据的一致性:LiveData 遵循观察者设计模式,生命周期发生变化时,LiveData 会通知对应的应用程序组件(Observer),数据发生变化时也会通知更新 UI.
  • 避免内存泄漏:这个 Observer 绑定了 Lifecycle 对象,当 Lifecycle 对象生命周期 destory 之后,这些 Observer 也会被自动清理。
  • 避免 Activity 处于不活跃状态的时候产生崩溃:如果观察者(Observer)处于不活跃状态,则 Observer 不会接收任何 LiveData 事件。
  • 不在手动处理生命周期:UI 组件只是观察相关数据,而不会停止或恢复观察,LiveData 会根据具体生命周期的变化而自动管理。
  • 始终保持最新数据:如果生命周期为非活跃状态,则会在由非活跃状态转为活跃状态时接收最新数据,如从后台切换到前台自动接收最新数据。
  • 正确处理配置更改:如果 Activity 或 Fragment 因为设备配置发生变化而重新创建,比如屏幕旋转等,也将会立即重新接收最新数据。
  • 共享服务:可以借助 LiveData 数据的观察能力,根据 Livecycle 的生命周期状态随时连接或断开服务。

二:添加依赖

在app的build.gradle里添加如下

def lifecycle_version = "2.2.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

三:使用LiveData

1 创建LiveData,一般我们创建LiveData是在ViewModel中完成。

class NameViewModel : ViewModel() {
    val currentName: MutableLiveData<String> by lazy { MutableLiveData<String>() }
}

我们定义一个NameViewModel集成ViewModel,并在ViewModel中声明MutableLiveData类型的变量currentName。

  • 注意:请确保用于更新界面的 LiveData 对象存储在 ViewModel 对象中,而不是将其存储在 Activity 或 Fragment 中,避免 Activity 和 Fragment 过于庞大,并且解耦。Activity 和 Fragment只负责显示数据,不负责存储数据状态。将 LiveData 实例与特定的 Activity 或 Fragment 实例分离开,并使 LiveData 对象在配置更改后继续存在。

2 在Activity/Fragment中创建Observer,并将其订阅上LiveData,当LiveData对象的数据更改时,会回调到Observer的onChanged方法。

class LiveDataTestActivity :AppCompatActivity(){

    private lateinit var model: NameViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_livedatatest)
        model = ViewModelProvider(this).get(NameViewModel::class.java)

        // 创建Observer
        val nameObserver = Observer<String> {
            tv_name.text = it
        }
        // 使用liveData的observer方法,将lifecycleOwner跟Observer作为参数传入,执行订阅
        model.currentName.observe(this,nameObserver)

        // 修改liveData的值,界面会自动更新UI
        tv_name.setOnClickListener{
            model.currentName.value = "名字叫:Chen Click"
        }
    }
}
  • val nameObserver = Observer { tv_name.text = it } 这是创建Observer,这里是lamda的写法,不使用lamda的写法是
    val nameObserver2 = object:Observer<String>{
      override fun onChanged(t: String?) {
      }
    }
    
    这是创建Observer,当LiveData有数据更改的时候,就会回调到该onChanged方法中
  • model.currentName.observe(this,nameObserver) 使用LiveData的observe方法,传入LifecycleOwner与observer去订阅LiveData。使用LifecycleOwner的好处是自己管理生命周期,会自动帮我们removeObserver。

注意:我们也可以使用 observeForever(Observer) 方法在没有关联的 LifecycleOwner 对象的情况下注册一个观察者。在这种情况下,观察者会被视为始终处于活跃状态,因此它始终会收到关于修改的通知。但我们必须手动去调用 removeObserver(Observer) 方法来移除这些观察者,否则会存在内存泄漏

class LiveDataTestActivity :AppCompatActivity(){
    private lateinit var model: NameViewModel
    private var nameObserver:Observer<String>?=null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_livedatatest)
        model = ViewModelProvider(this).get(NameViewModel::class.java)

        // 创建Observer
        nameObserver = Observer<String> {
            tv_name.text = it
        }
        // 使用observeForever需要自己在合适的地方removeObserver,否则会存在内存泄漏
        model.currentName.observeForever(nameObserver!!)
        // 修改liveData的值,界面会自动更新UI
        tv_name.setOnClickListener{
            model.currentName.value = "名字叫:Chen Click"
        }
    }

    override fun onDestroy() {
        model.currentName.removeObserver(nameObserver!!)
        super.onDestroy()
    }
}

使用LiveData的observeForever方法,需要在onDestroy的时候,model.currentName.removeObserver(nameObserver!!)。避免内存泄漏。

3 更新LiveData的值,UI界面自动更新

tv_name.setOnClickListener{
   model.currentName.value = "名字叫:Chen Click"
}
  • 我们可以使用MutableLiveData的setValue或者postValue方法去修改LiveData的值,修改完后界面会自动更新UI 注意:您必须调用 setValue(T) 方法以从主线程更新 LiveData 对象,而postValue(T)是在可以用在子线程中更新LiveData

四:自定义LiveData

继承LiveData,重写onActive跟onInactive方法,下面例子,监听网络变化

class NetWorkLiveData(val context: Context) :LiveData<String>(){

    companion object{
        const val NETWORK_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"
    }

    private val intentFilter = IntentFilter().apply {
        addAction(NETWORK_ACTION)
    }
    private val mReceiver =object:BroadcastReceiver(){
        override fun onReceive(context: Context?, intent: Intent?) {
            if(intent?.action == NETWORK_ACTION){
                value = "网络状态改变"
            }
        }
    }

    override fun onActive() {
        context.registerReceiver(mReceiver,intentFilter)
        super.onActive()
    }

    override fun onInactive() {
        context.unregisterReceiver(mReceiver)
        super.onInactive()
    }
}
  • onActive() 当有一个处于活跃状态的观察者监听LiveData时会被调用,我们的例子是表示注册了网络监听
  • onInactive():当没有任何处于活跃状态的观察者监听LiveData时会被调用。我们这里例子是由于没有监听了,所以就取消注册网络监听的广播。
  • setValue():更新LiveData的值,并通知到观察者。上面例子调用是value = "网络状态改变"

在Activity中使用自定义的LiveData,例子如下:

class LiveDataTestActivity :AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_livedatatest)
        
        NetWorkLiveData(this).observe(this, Observer {
            tv_name.text = it
        })
    }
}

NetWorkLiveData(this).observe(this, Observer { tv_name.text = it }) 添加如上代码,这样改变网络状态,那么text的值就会改变。

五:LiveData转换

有时候LiveData存储的类型不是我们想要的类型,这时候可以考虑使用转换,转成我们需要的类型,LiveData转换主要有两种变换:map和switchMap,都是Transformations类提供的。

5.1:map转换

class TestViewModel :ViewModel(){
    val ageLiveData = MutableLiveData<Int>()
    val nameLiveData = Transformations.map(ageLiveData, Function<Int,String> {
        "名字是$it"
    })
}
  • 上面例子是将Int转成String的LiveData,map是直接修改返回的值和类型

5.2:switchMap转换

class TestViewModel :ViewModel(){
    val ageLiveData = MutableLiveData<Int>()
    fun getGradeName(age:Int):LiveData<String>{
        val liveData = MutableLiveData<String>()
        liveData.value = "年级是$age"
        return liveData
    }
    val gradeLiveData = Transformations.switchMap(ageLiveData,Function<Int,LiveData<String>>{
        getGradeName(it)
    })
}
  • 上面例子是switchMap将Int转成String的,switchMap变换需要返回一个LiveData对象,这就是跟map变换的区别。

六:合并多个LiveData数据

如果有多个LiveData,可以使用MediatorLiveData合并。一旦其中一个发生改变,就会通知MediatorLiveData。举例如下:

class NameViewModel : ViewModel() {
    val currentName: MutableLiveData<String> by lazy { MutableLiveData<String>() }
    val courseName: MutableLiveData<String> by lazy { MutableLiveData<String>() }
    val media:MediatorLiveData<String> by lazy { MediatorLiveData<String>() }
    init {
        media.addSource(currentName) { media.value = it }
        media.addSource(courseName) { media.value = it }
    }
}
  • media 通过addSource添加上currentName跟courseName这两个liveData 在Activity中使用
class LiveDataTestActivity :AppCompatActivity(){

    private lateinit var model: NameViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_livedatatest)
        model = ViewModelProvider(this).get(NameViewModel::class.java)
        // 创建Observer
        val nameObserver = Observer<String> {
            if(it.startsWith("name")){
                tv_name.text = it
            }else{
                tv_course_name.text = it
            }
        }
        model.media.observe(this,nameObserver)

        // 修改liveData的值,界面会自动更新UI
        tv_name.setOnClickListener{
            model.currentName.value = "name 名字叫:Chen Click"
        }
        tv_course_name.setOnClickListener{
            model.courseName.value = "course 课程叫:数学"
        }
    }
}
  • model.media.observe(this,nameObserver) 添加观察。
  • model.currentName.value = "name 名字叫:Chen Click"。只要currentName值一改变就会通知到media注册的nameObserver
  • model.courseName.value = "course 课程叫:数学"。只要courseName值一改变也会通知到media注册的nameObserver

这就是MediatorLiveData的用法,通过addSource合并多个LiveData的数据。可以通过removeSource删除LiveData的数据。举个简单removeSource的例子

mediatorLiveData.addSource(liveData1, new Observer() {
      private int count = 1;
      @Override public void onChanged(@Nullable Integer s) {
          count++;
          mediatorLiveData.setValue(s);
          if (count > 10) {
              mediatorLiveData.removeSource(liveData1);
          }
      }
 });

以上就是LiveData的所有支持点