Jetpack--LiveData

  • LiveData 速描

  • 资料来源:

    https://developer.android.com/topic/libraries/architecture/livedata

  • 更新

    1
    20.02.24 初始化

导语

  • 开始刷 Jetpack 其他部分了.初期还是官方文档总结.
  • 导入 LiveData 不涉及了.
  • 怎么感觉坑反而越来越多了😂

基础

  • livedata 优势

    • 最主要的一条是具有生命周期感知能力,只有绑定的观察者(Activity/Fragment)在活跃状态, livedata 才会更新数据.避免了内存泄漏.
    • 观察者被销毁时,livedata 的绑定会自动解除.
    • 配置改变(设备旋转等)重新创建 Activity/Fragment livedata 会立刻接收最新数据.
    • 使用单例模式拓展 livedata 对象封装系统服务,便于共享.
  • 使用 livedata

    • 创建 livedata 的实例,通常在 ViewModel 中.
    • 创建可定义 onChanged() 方法的 Observer 对象,该方法可以控制当 LiveData 对象存储的数据更改时会发生什么.通常情况是在在界面控制器 (Activity/Fragment) 中创建.
    • 使用 observe() 方法将 Observer 对象订阅 LiveData 对象.这样在 livedata 发生变化且 Observer 对象处于活跃状态时收到通知.
    • ps:可以使用 observeForever(Observer) 方法来注册未关联 LifecycleOwner 对象的观察者.这样观察者会被视为始终处于活跃状态,因此它始终会收到关于修改的通知.您可以通过调用 removeObserver(Observer) 方法来移除这些观察者.
  • 创建 livedata 对象

    • LiveData 是数据封装容器,可以封装任何数据类型.包括 List 等.通常用在 ViewModel.(关于ViewModel 又是另外一篇了)

    • 例:

      1
      2
      3
      4
      5
      6
      7
      class NameViewModel : ViewModel() {
      // Create a LiveData with a String
      val currentName: MutableLiveData<String> by lazy {
      MutableLiveData<String>()
      }
      // Rest of the ViewModel...
      }
  • 观察 LiveData

    • 通常应用组件的 onCreate() 方法是开始观察 LiveData 对象的起点:

      • Activity/Fragment 的 onResume() 在整个生命周期可能会调用多次,并不适合作为订阅的起点.
      • 可以确保 Activity/Fragment 变为活跃状态后可以立即显示的数据.
    • 通常 LiveData 只有在数据更改时才发送更新给活跃的观察者.当观察者从非活跃状态转换为活跃状态时也会收到 LiveData 更新,但是当观察者第二次由非活跃转入活跃且 LiveData 的值并没有发生变化时并不会收到更新.

    • 例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
        class NameActivity : AppCompatActivity() {
      private lateinit var model: NameViewModel
      override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      // 设置 ViewModel.
      model = ViewModelProviders.of(this).get(NameViewModel::class.java)
      // 实例化 observer 对象
      val nameObserver = Observer<String> { newName ->
      // Update the UI, in this case, a TextView.
      nameTextView.text = newName
      }
      // 绑定 LiveData
      model.currentName.observe(this, nameObserver)
      }
      }
  • 更新 LiveData

    • LiveData 没有公开可用的方法来更新存储的数据.MutableLiveData 类将公开 setValue(T) 和 postValue(T) 方法,如果您需要修改存储在 LiveData 对象中的值,则必须使用这些方法.
    • 通常在 ViewModel 中使用 MutableLiveData,然后 ViewModel 只会向观察者公开不可变的 LiveData 对象.
    • setValue(T) 从主线程更新 LiveData 对象. postValue(T) 可以在其他线程调用来更新 LiveData 对象.
  • LiveData 与 Room 配合

    • 暂且不表.简而言之 Room 支持直接返回一个 LiveData.
  • LiveData 与 kotlin 协程(非常赞!!)

    • 详情见 将协程与 LiveData 一起使用
    • liveData 构建器函数调用 suspend 函数,并将结果作为 LiveData 对象传送.

扩展 LiveData

  • 可以自定义类继承自 LiveData ,改变 LiveData 在观察对象活跃非活跃时的一些动作.

    • onActive(): LiveData 对象具有活跃观察者时调用 onActive().
    • onInactive(): LiveData 对象没有任何活跃观察者时调用 onInactive(),由于没有观察者在监听,需要与 StockManager 服务断开连接.
  • 例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
    value = price
    }

    override fun onActive() {
    stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
    stockManager.removeUpdates(listener)
    }
    }
  • 使用 StockLiveData 与使用普通 LiveData 完全相同.

  • LiveData 对象具有生命周期感知能力,这意味着您可以在多个 Activity/Fragment/Service 之间共享同一 LiveData 实例.一个好的方式是实现一个单例 LiveData.(kotlin 中使用伴生对象非常方便)

    • 例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
      // do something
      ...

      companion object {
      private lateinit var sInstance: StockLiveData

      @MainThread
      fun get(symbol: String): StockLiveData {
      sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
      return sInstance
      }
      }
      }

转换/合并 LiveData

  • 个人感觉这个非常类似 rxjava 的流式处理…不知道是不是错觉…

  • LiveData 对象分派给观察者之前需要对值进行更改,或者您可能需要根据另一个实例的值返回不同的 LiveData 实例.Lifecycle 软件包会提供 Transformations 类.

    • Transformations.map(): 对每个值应用 lambda,并将结果返回.

      1
      2
      3
      4
      val userLiveData: LiveData<User> = UserLiveData()
      val userName: LiveData<String> = Transformations.map(userLiveData) {
      user -> "${user.name} ${user.lastName}"
      }
    • Transformations.switchMap(): 与 map() 类似,对存储在 LiveData 对象中的值应用函数,并将结果解封和分派到下游.传递给 switchMap() 的函数必须返回 LiveData 对象.

      1
      2
      3
      4
      5
      private fun getUser(id: String): LiveData<User> {
      ...
      }
      val userId: LiveData<String> = ...
      val user = Transformations.switchMap(userId) { id -> getUser(id) }
  • 可以使用转换方法在观察者的生命周期内传送信息.除非观察者正在观察返回的 LiveData 对象,否则不会计算转换.因为转换是以延迟的方式计算,所以与生命周期相关的行为会隐式传递下去,而不需要额外的显式调用或依赖项.

    • 这段话有点费解.举个例子:

    • 例如这段代码是 ViewModel 中一个地址和邮编的转换.(千万别这么写…)

      1
      2
      3
      4
      5
      6
      7
          class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {

      private fun getPostalCode(address: String): LiveData<String> {
      // DON'T DO THIS
      return repository.getPostCode(address)
      }
      }
    • LiveData 对应的观察实例重建后会向实例返回 LiveData 的值,但是向上面的写法,等于每次都再调用 repository.getPostCode(address) 而不是返回之前已经得到的结果,这样就违背了使用 LiveData 的原则.

    • 写成下面这个样子,就算观察实例重建,只要 addressInput 没有发生更改,就不会重复调用 repository.getPostCode(address).观察实例重建只会返回之前的结果.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {
      private val addressInput = MutableLiveData<String>()
      val postalCode: LiveData<String> = Transformations.switchMap(addressInput) {
      address -> repository.getPostCode(address) }

      private fun setInput(address: String) {
      addressInput.value = address
      }
      }
  • 这些转换也可以自定义,需要使用 MediatorLiveData 类.该类可以监听其他 LiveData 对象并处理它们发出的事件,MediatorLiveData 正确地将其状态传播到源 LiveData 对象.要详细了解此模式,请参阅 Transformations 类的参考文档.(占个坑,后面补)

  • 合并多个 LiveData 源,大概会在涉及数据请求,合并网络的信息和本地数据库的信息时用到.(占坑,后面填)