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
})
效果如下,每次點,都有不同的數字