在上篇講了下ViewModel 這次接著講LiveData
下一篇 Jetpack mvvm 三部曲(三) DataBinding
先放下本jetpak系列在學(xué)習(xí)過(guò)程寫(xiě)的demojetpackDemo
先貼下官方的鏈接LiveData
根據(jù)官方給的說(shuō)發(fā)LiveData可為數(shù)據(jù)提供觀察者
(就是一個(gè)接口)
,在數(shù)據(jù)進(jìn)行改變的時(shí)候活躍的觀察者可以獲取到數(shù)據(jù)的變化非活躍的觀察者是不能收到變化通知的-
我在這里寫(xiě)了個(gè)計(jì)時(shí)器先記錄生命周期的狀態(tài)以及LiveData數(shù)值,當(dāng)數(shù)據(jù)產(chǎn)生變化打印看下生命周期方法以及當(dāng)前的數(shù)值,可以看到在app回到桌面進(jìn)入到onPause后觀察者就收不到通知了,而重新從后臺(tái)回到app進(jìn)入onResume狀態(tài)觀察者又能收到通知了。
log LiveData能做到這點(diǎn)還是要?dú)w功于LifecycleOwner接口,當(dāng)然AndroidX的AppCompatActivity類(lèi)已經(jīng)幫我們實(shí)現(xiàn)了LifecycleOwner接口
先看下怎么實(shí)現(xiàn)在看下源碼
引用(2.2.0目前是最新版本) 其余版本可參考
implementation 'androidx.lifecycle:lifecycle-livedata:2.2.0'
- 鑒于官方的提醒所以這次例子就是ViewModel+LiveData了,下面創(chuàng)建一個(gè)ViewModel,如果不了解可以看我上一篇文章Jetpack mvvm 三部曲(一) ViewModel
public class MyViewModel extends ViewModel {
public MutableLiveData<String> stringMediatorLiveData;
public MyViewModel() {
stringMediatorLiveData = new MutableLiveData<>();
}
}
- 實(shí)現(xiàn)監(jiān)聽(tīng)LiveData數(shù)據(jù)的變化使用observe方法傳入LifecycleOwner,在實(shí)現(xiàn)觀察者Observer
接口
就行了,通過(guò)setValue 或者 postValue改變LiveData的值就能在Observer接口的onChanged方法收到改變后的數(shù)值了。 -
這里有一個(gè)注意的點(diǎn)在主線程使用可以用setValue,如果是子線程改變值用postValue,因?yàn)樵诙嗑€程中操作數(shù)據(jù)是不安全的,而postValue是通過(guò)synchronized關(guān)鍵字確保線程安全
官方提醒
postValue方法
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.stringMediatorLiveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.e("MainActivity",s+"----");
}
});
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
viewModel.stringMediatorLiveData.setValue("哈哈哈哈");
}
});
-
實(shí)現(xiàn)的代碼敲完了,接著看下源碼。
圖1
圖2
圖3
圖4
圖5
圖6
圖7
圖8
圖9 圖一是傳LifecycleOwner過(guò)去
圖二是傳遞保存在一個(gè)map對(duì)象中
圖3是把LifecycleObserver拿到更新生命周期狀態(tài)
圖4是回調(diào)LiveDataon中LifecycleBoundObserver類(lèi)的StateChanged方法見(jiàn)圖5
圖6調(diào)用dispatchingValue改變值的時(shí)候會(huì)通過(guò)mObservers這個(gè)map對(duì)象進(jìn)行遍歷見(jiàn)圖7,從map集合中取出觀察者
ObserverWrapper接口
調(diào)用onChanged方法進(jìn)行回調(diào)通知注意圖8的第一個(gè)紅框return
如果observer.mActiveactivity的活躍狀態(tài)
為false就直接不走下面的方法了,這個(gè)mAtive參數(shù)的賦值見(jiàn)圖5 activeStateChanged(shouldBeActive()); shouldBeActive這個(gè)方法見(jiàn)圖9
-------------------------------分割線-------------------------------
- 到這里L(fēng)iveData你就有了初步的認(rèn)知,在上面的例子是直接通過(guò)MutableLiveData去實(shí)現(xiàn)的。
-
MutableLiveData既可以去改變值也可以監(jiān)聽(tīng)值的變化但是為了數(shù)值的維護(hù)客觀性(ps.不要直接通過(guò)MutableLiveData去setValue如果多個(gè)地方改變值不太容易看出來(lái)不方便后期),我們可以通過(guò)LiveData去寫(xiě)(MutableLiveData其實(shí)是繼承自LiveData只不過(guò)就是暴露了下setValue跟postValue方法)
- 鑒于LiveData的setValue和postValue方法修飾關(guān)鍵詞是protected
(ps.包名不同無(wú)法調(diào)用該方)
,我們可以這么去實(shí)現(xiàn)。
public class MyViewModel extends ViewModel {
private MutableLiveData<String> stringMediatorLiveData;
public LiveData<String> stringLiveData;
public MyViewModel() {
stringMediatorLiveData = new MutableLiveData<>();
stringLiveData = stringMediatorLiveData;
this.user = new User("張三",11);
this.count = 13;
}
public void update(String s){
stringMediatorLiveData.setValue(s);
}
}
- 使用
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.stringLiveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.e("MainActivity",s+"----");
}
});
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
viewModel.update("哈哈哈哈");
}
});
- 雖然多寫(xiě)了寫(xiě)代, 但是從代碼維護(hù)來(lái)看更合理寫(xiě)(ps.官方也是建議這么做的)
-------------------------------分割線-------------------------------
如果就想去實(shí)時(shí)監(jiān)聽(tīng)數(shù)據(jù)更新不管頁(yè)面可見(jiàn)不可見(jiàn)那怎么辦
- 這個(gè)時(shí)候observeForever方法就出馬了
- 看了上面得源碼部分就應(yīng)該知道LiveData的observe接口是怎么根據(jù)可見(jiàn)不可見(jiàn)狀態(tài)被調(diào)用的
-
observeForever方法直接把狀態(tài)改成了全程可見(jiàn),而不像observe方法由LifecycleBoundObserver根據(jù)shouldBeActive方法去設(shè)定了。
image.png
image.png
image.png - 把observe改成observeForever方法就能實(shí)時(shí)去監(jiān)聽(tīng)數(shù)值變化了,畢竟observeForever直接把狀態(tài)設(shè)置成可見(jiàn)了,源碼那快圖8那個(gè)considerNotify方法就不會(huì)因?yàn)椴豢梢?jiàn)狀態(tài)return調(diào)了不去執(zhí)行下面接口回調(diào)的方法了
Observer<Integer> integerObserver = new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Log.e("LiveDataActivity",string+"--->"+integer);
}
};
model.time.observeForever(integerObserver);
-
注意observeForever要自己去removeObserver掉監(jiān)聽(tīng),因?yàn)閛bserve方法中LifecycleBoundObserver自己處理了
image.png
在扯下MediatorLiveData
MediatorLiveData是MutableLiveData的子類(lèi)
-
MutableLiveData這個(gè)類(lèi)是繼承LiveData把postValue和setValue暴露出來(lái)
image.png MediatorLiveData這個(gè)就有點(diǎn)意思了正如他的名字Mediator(中介的意思)
-
MediatorLiveData的核心是私有靜態(tài)內(nèi)部類(lèi)Source
image.png 先記住紅框的部分很重要,addSource就是把另一個(gè)LiveData由Source進(jìn)行代理,監(jiān)聽(tīng)當(dāng)被代理的LiveData數(shù)值發(fā)生改變那么他就會(huì)去回調(diào)傳入的觀察者onChanged方法
原理很簡(jiǎn)單那有什么用呢
-
假設(shè)下面的場(chǎng)景實(shí)時(shí)知道文本框輸入了多少個(gè)字
image.png
public MutableLiveData<String> message = new MutableLiveData<>();
public MediatorLiveData<Integer> messageNumber = new
MediatorLiveData<>();
messageNumber.setValue(0);
messageNumber.addSource(message, new Observer<String>() {
@Override
public void onChanged(String s) {
messageNumber.setValue(s.length());
}
});
<LinearLayout
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
<EditText
android:id="@+id/edit"
android:layout_width="200dp"
android:layout_marginLeft="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="@={mode.message}"
android:singleLine="true"
android:background="@drawable/b2"
android:layout_height="50dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:textColor="#cc00"
android:text='@{String.format("輸入了%d個(gè)字",mode.messageNumber)}'
android:layout_height="wrap_content"/>
</LinearLayout>
- 這樣實(shí)現(xiàn)就行了
- 當(dāng)然絕對(duì)不能忘記注銷(xiāo)LiveData的監(jiān)聽(tīng),原因嘛在observeForever也講了的,我們?nèi)iewModel的onCleared方法做就好了,畢竟onCleared是ViewModel執(zhí)行的最后一個(gè)方法。
@Override
protected void onCleared() {
super.onCleared();
messageNumber.removeSource(message);
}
總結(jié)下
- 普通數(shù)據(jù)使用MutableLiveData和LiveData就行了
- 設(shè)計(jì)到數(shù)據(jù)聯(lián)動(dòng)的時(shí)候考慮下MediatorLiveData
最后在說(shuō)1個(gè)東西轉(zhuǎn)換
- 在項(xiàng)目中我們經(jīng)常回運(yùn)到數(shù)據(jù)轉(zhuǎn)換的問(wèn)題比如int轉(zhuǎn)換成string或者int轉(zhuǎn)換成另一個(gè)對(duì)象
map
public class LiveDataViewModel extends ViewModel {
private MutableLiveData<User> userMutableLiveData;
public LiveData<Integer> age;
private List<User> users;
public LiveDataViewModel() {
userMutableLiveData = new MutableLiveData<>();
age = Transformations.map(userMutableLiveData, new Function<User, Integer>() {
@Override
public Integer apply(User input) {
return input.getAge();
}
});
public void addUser(User user){
//這里值得注意的是setValue主線程用沒(méi)問(wèn)題,如果是子線程那就要用postValue
// userMutableLiveData.postValue(user);
if(Looper.myLooper() == Looper.getMainLooper()){
userMutableLiveData.setValue(user);
}else {
userMutableLiveData.postValue(user);
}
}
}
- 這時(shí)直接去監(jiān)聽(tīng)age就行了,只要userMutableLiveData的值有改變我們就能立馬監(jiān)聽(tīng)到User類(lèi)中的年齡了
model.age.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Log.e("MainActivity",integer+"--");
}
});
switchMap
- switchMap和map同樣屬于數(shù)據(jù)轉(zhuǎn)換的一種,在實(shí)際項(xiàng)目中switchMap使用的頻率回比map更高map是通過(guò)監(jiān)聽(tīng)一個(gè)對(duì)象的變換然后進(jìn)行轉(zhuǎn)換的,假設(shè)有另外一個(gè)場(chǎng)景通過(guò)用戶id進(jìn)行網(wǎng)絡(luò)請(qǐng)求去拿到這個(gè)用戶的信息,按著map的寫(xiě)法豈不是要有好幾個(gè)步驟先通過(guò)網(wǎng)絡(luò)請(qǐng)求去拿到user對(duì)象、再去賦值給LiveData、在轉(zhuǎn)換給LiveData、在對(duì)LiveData進(jìn)行監(jiān)聽(tīng)改變ui。
- 下面直接模擬下子線程查詢進(jìn)行用戶查詢
public class LiveDataViewModel extends ViewModel {
public LiveData<User> userLiveData;
private MutableLiveData<Integer> userIndex = new MutableLiveData<>();
private List<User> users;
public LiveDataViewModel() {
users = new ArrayList<>();
users.add(new User("李四",16));
users.add(new User("張三",18));
users.add(new User("王武",17));
users.add(new User("錢(qián)六",20));
userLiveData = Transformations.switchMap(userIndex, new Function<Integer, LiveData<User>>() {
@Override
public LiveData<User> apply(Integer input) {
return getUser(input);
}
});
}
public void queryUser(int userId){
userIndex.setValue(userId);
}
//模擬查詢
public LiveData<User> getUser(int userId){
MutableLiveData<User> userMutableLiveData = new MutableLiveData<>();
if(userId>users.size()-1){
userMutableLiveData.setValue(new User("沒(méi)有該用戶",0));
return userMutableLiveData;
}
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
userMutableLiveData.postValue(users.get(userId));
}
}).start();
return userMutableLiveData;
}
}
- 監(jiān)聽(tīng)
model.userLiveData.observe(this, new Observer<User>() {
@Override
public void onChanged(User user) {
dataBinding.name.setText("名字:"+user.getName());
dataBinding.tvAge.setText("年齡:"+user.getAge());
}
});
- 當(dāng)調(diào)用了queryUser方法就會(huì)去觸發(fā)switchMap方法中的Function接口apply方法去查詢用戶數(shù)據(jù)了
最后的最后說(shuō)下onChanged回調(diào)是在主線程的,不然會(huì)拋異常這也解釋了子線程得用postValue去更新數(shù)值
-
setValue
image.png
image.png -
postValue
image.png
image.png