初探Jetpack(三) -- LiveData

初探Jetpack(一) – ViewModel
初探Jetpack(二) – Lifecycles

LiveData 是 Jetpack 提供的一种响应式变成组件,它可以包含任何类型的数据,并在数据发现变化的时候,通知观察者。

LiveData 具有生命周期的感知能力,指它能感觉 activity,fragment ,service 的生命周期,且只有这些组件处于活跃生命周期状态时,LiveData 感觉到数据变化,才会发出通知。

什么意思呢?即如果 activity ,fragment 等观察者处于生命周期的 STARTED, ONRESUMED状态,则 LiveData 会认为该观察者处于活跃状态,LiveData 就会将最近的通知给活跃的观察者,而非活跃状态的观察者则收不到通知。

所以,当我们把 LiveData 和 有 LifecycleOwner 接口对象结合,当 Lifecycle 的对象状态为 DESTROYED 的时候,便可移除此观察者。 这对于 Activity 和 Fragment 特别有用,因为它们可以放心地观察 LiveData 对象而不必担心泄露(当 Activity 和 Fragment 的生命周期被销毁时,系统会立即退订它们)。

即我们不用手动去处理生命周期,它会自动管理这些操作。

一、基本使用

创建 LiveData 实例以存储某种类型的数据。这通常在 ViewModel 类中完成。
在第一张章,我们学习了 ViewModel 的计时器功能 初探Jetpack(一) – ViewModel,这里再使用 LiveData 对它进行扩展:

class DataViewModel() :ViewModel() {

    private val _counter = MutableLiveData<Int>()

    val counter : LiveData<Int> get() = _counter;
    init {
        _counter.value = 0;
    }
    fun plusOne(){
        val size = _counter.value ?: 0;
        _counter.value = size+1;
    }
}

从上面看到,我们申明了一个 MutableLiveData 对象,并指定泛型为 Int,表示它的数据为整型。

MutableLiveData 是一种可变的 LiveData,有三个方法,分别是 getValue(),setValue()和 postValue()。

  • getValue() : 用于获取 LiveData 的数据
  • setValue() : 给 LiveData 设置数据,只能在主线程
  • postValue(): 可以在非主线程中给LiveData 设置数据
// 这里用了 getValue 和 setValue 的语法糖
  val size = _counter.value ?: 0;
 _counter.value = size+1;

为了不让外面调用 MutableLiveData,把它设置成 private,且公布出去的 counter 是 MutableLiveData 的 get() 属性方法的返回值,这样 _counter 变量就对外面不可见了。

后面我们在activity 中,这样写:

        val viewModel = ViewModelProvider(this).get(DataViewModel::class.java)

        plusOne.setOnClickListener{
            //点击增加
            viewModel.plusOne()
        }
        //添加数据观察者
        val textObserver = Observer<Int> { count ->
            infoText.text = count.toString()
        }
        viewModel.counter.observe(this,textObserver)

注意看到,使用了 MutableLiveData 的observe方法,它接受两个参数,第一个参数为 LifecycleOwner,直接传递 this 即可,第二是是 observe 接口,当数据发生变化时,就会毁掉这里的接口。我们把数据更新到 textview 即可,效果如下:
在这里插入图片描述

第一行代码 第三版
官网 LifeData

二、 map 和 switchMap

LiveData 为了能够应对各种不同的需求场景,提供了 map 和 switchMap 两种转换方法。

2.1 map

先看 map,这个方法的作用是将实际包含 LiveData 的数据和仅用于观察数据的 LiveData 进行转换。什么意思呢?比如我新建了一个类,User:

data class User (var firstName:String,var lastName:String,var age:Int)

然后新建一个 LiveData 来包含 User 数据:

class UserViewModel : ViewModel() {
    private val userViewModel = MutableLiveData<User>()

}

这样看,跟上面好像没啥区别,但是 activity 中,只想显示 name,不要 age,怎么办呢?

这个时候,就可以使用 map(),它可以将 User 类型的 LiveData 自由地转型成任意其他类型的 LiveData ,如下:

class UserViewModel : ViewModel() {
    private val userLiveData = MutableLiveData<User>()

    val userName:LiveData<String> = Transformations.map(userLiveData){
        user -> "${user.firstName} ${user.lastName}"
    }

    fun getUserName(user: User){
        userLiveData.value = user;
    }
}

可以看到,我们使用了 Transformations.map() 方法来进行 LiveData 的类型转换,它接受两个参数,第一个 LiveData,第二个就是当 userLiveData 数据发生变化时,需要转换的逻辑,这里直接拿到它的 firstName 和 lastName。
ok,调用我们这样调用:

        val viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        plusOne.setOnClickListener{
            viewModel.getUserName(User("zhang","san",2))
        }

        //添加数据观察者
        viewModel.userName.observe(this, Observer { string ->
            infoText.text = string
        })

在click的时候,调用 getUserName,让数据发生变化,这里的 Observer 只需要知道它的string 即可:
在这里插入图片描述

2.2 switchMap

如果 ViewModel 中的某个 LiveData 对象是调用另外的方法获取的,则可以借助 switchMap 方法,将这个 LiveData 转换成可观察的 LiveData 对象。

什么意思呢?比如,上面的例子中,LiveData 都是在 ViewModel 中创建,但是不可能是这种理想情况,如果在外部创建呢,新建一个单利类,让它返回 livedata:

object Repository {
    fun getUser(name:String):LiveData<User>{
        val liveData = MutableLiveData<User>()
        liveData.value = User(name,name,0)
        return liveData;
    }
}

这里其实模仿的是,根据name,拿到一个数据库的 User 数据,这里直接新建一个 User 即可。

然后,修改一下 UserViewModel:

class UserViewModel : ViewModel() {
    

    fun getUser(name:String) : LiveData<User>{
        return Repository.getUser(name)
    }
}

获取了 LiveData 对象,那接下来呢?如何观察数据?如下写法?:

     viewModel.getUser("test").observe(this, Observer { user ->
             
         })

这肯定是不行的!!!每次 getUser 返回都是一个新的 liveData,而上述写法会一直观察老的 LiveData 实例,根本无法观察数据的变化。

这个时候,就可以使用 switchMap 了,如下修改:

class UserViewModel : ViewModel() {
    private val userLiveData = MutableLiveData<String>()
    val user : LiveData<User> = Transformations.switchMap(userLiveData){
        name -> Repository.getUser(name)
    }

    fun  getUser(name: String){
        userLiveData.value = name;
    }
}

可以看到,我们定义了一个 string 类型的 MutableLiveData对象,用来观察 name 的变化,然后调用 Transformations 的 switchMap 方法。它接收两个参数,第一个为LiveData ,传入userLiveData ,第二个参数时一个转换函数,注意!这个转换函数必须返回一个 LiveData 对象,这里返回时通过 Repository 返回的一个新的 LiveData 数据。

这个每次 userLiveData.value 的值发生变化,就会调用 switchMap 的转换函数啦。

在activity 中这样调用:

        val viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        plusOne.setOnClickListener{
            val name = (0..100).random().toString()
            viewModel.getUser(name)
        }

        viewModel.user.observe(this, Observer { user ->
            infoText.text = user.firstName
        })

效果如下,每次点,都有不同的数字
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章