初探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
        })

效果如下,每次點,都有不同的數字
在這裏插入圖片描述

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