在上篇講了下ViewModel 這次接著講LiveData
下一篇 Jetpack mvvm 三部曲(三) DataBinding
先放下本jetpak系列在學習過程寫的demojetpackDemo
先貼下官方的鏈接LiveData
根據官方給的說發LiveData可為數據提供觀察者
(就是一個接口)
,在數據進行改變的時候活躍的觀察者可以獲取到數據的變化非活躍的觀察者是不能收到變化通知的-
我在這里寫了個計時器先記錄生命周期的狀態以及LiveData數值,當數據產生變化打印看下生命周期方法以及當前的數值,可以看到在app回到桌面進入到onPause后觀察者就收不到通知了,而重新從后臺回到app進入onResume狀態觀察者又能收到通知了。
log LiveData能做到這點還是要歸功于LifecycleOwner接口,當然AndroidX的AppCompatActivity類已經幫我們實現了LifecycleOwner接口
先看下怎么實現在看下源碼
引用(2.2.0目前是最新版本) 其余版本可參考
implementation 'androidx.lifecycle:lifecycle-livedata:2.2.0'
- 鑒于官方的提醒所以這次例子就是ViewModel+LiveData了,下面創建一個ViewModel,如果不了解可以看我上一篇文章Jetpack mvvm 三部曲(一) ViewModel
public class MyViewModel extends ViewModel {
public MutableLiveData<String> stringMediatorLiveData;
public MyViewModel() {
stringMediatorLiveData = new MutableLiveData<>();
}
}
- 實現監聽LiveData數據的變化使用observe方法傳入LifecycleOwner,在實現觀察者Observer
接口
就行了,通過setValue 或者 postValue改變LiveData的值就能在Observer接口的onChanged方法收到改變后的數值了。 -
這里有一個注意的點在主線程使用可以用setValue,如果是子線程改變值用postValue,因為在多線程中操作數據是不安全的,而postValue是通過synchronized關鍵字確保線程安全
官方提醒
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("哈哈哈哈");
}
});
-
實現的代碼敲完了,接著看下源碼。
圖1
圖2
圖3
圖4
圖5
圖6
圖7
圖8
圖9 圖一是傳LifecycleOwner過去
圖二是傳遞保存在一個map對象中
圖3是把LifecycleObserver拿到更新生命周期狀態
圖4是回調LiveDataon中LifecycleBoundObserver類的StateChanged方法見圖5
圖6調用dispatchingValue改變值的時候會通過mObservers這個map對象進行遍歷見圖7,從map集合中取出觀察者
ObserverWrapper接口
調用onChanged方法進行回調通知注意圖8的第一個紅框return
如果observer.mActiveactivity的活躍狀態
為false就直接不走下面的方法了,這個mAtive參數的賦值見圖5 activeStateChanged(shouldBeActive()); shouldBeActive這個方法見圖9
-------------------------------分割線-------------------------------
- 到這里LiveData你就有了初步的認知,在上面的例子是直接通過MutableLiveData去實現的。
-
MutableLiveData既可以去改變值也可以監聽值的變化但是為了數值的維護客觀性(ps.不要直接通過MutableLiveData去setValue如果多個地方改變值不太容易看出來不方便后期),我們可以通過LiveData去寫(MutableLiveData其實是繼承自LiveData只不過就是暴露了下setValue跟postValue方法)
- 鑒于LiveData的setValue和postValue方法修飾關鍵詞是protected
(ps.包名不同無法調用該方)
,我們可以這么去實現。
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("哈哈哈哈");
}
});
- 雖然多寫了寫代, 但是從代碼維護來看更合理寫(ps.官方也是建議這么做的)
-------------------------------分割線-------------------------------
如果就想去實時監聽數據更新不管頁面可見不可見那怎么辦
- 這個時候observeForever方法就出馬了
- 看了上面得源碼部分就應該知道LiveData的observe接口是怎么根據可見不可見狀態被調用的
-
observeForever方法直接把狀態改成了全程可見,而不像observe方法由LifecycleBoundObserver根據shouldBeActive方法去設定了。
image.png
image.png
image.png - 把observe改成observeForever方法就能實時去監聽數值變化了,畢竟observeForever直接把狀態設置成可見了,源碼那快圖8那個considerNotify方法就不會因為不可見狀態return調了不去執行下面接口回調的方法了
Observer<Integer> integerObserver = new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Log.e("LiveDataActivity",string+"--->"+integer);
}
};
model.time.observeForever(integerObserver);
-
注意observeForever要自己去removeObserver掉監聽,因為observe方法中LifecycleBoundObserver自己處理了
image.png
在扯下MediatorLiveData
MediatorLiveData是MutableLiveData的子類
-
MutableLiveData這個類是繼承LiveData把postValue和setValue暴露出來
image.png MediatorLiveData這個就有點意思了正如他的名字Mediator(中介的意思)
-
MediatorLiveData的核心是私有靜態內部類Source
image.png 先記住紅框的部分很重要,addSource就是把另一個LiveData由Source進行代理,監聽當被代理的LiveData數值發生改變那么他就會去回調傳入的觀察者onChanged方法
原理很簡單那有什么用呢
-
假設下面的場景實時知道文本框輸入了多少個字
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個字",mode.messageNumber)}'
android:layout_height="wrap_content"/>
</LinearLayout>
- 這樣實現就行了
- 當然絕對不能忘記注銷LiveData的監聽,原因嘛在observeForever也講了的,我們去ViewModel的onCleared方法做就好了,畢竟onCleared是ViewModel執行的最后一個方法。
@Override
protected void onCleared() {
super.onCleared();
messageNumber.removeSource(message);
}
總結下
- 普通數據使用MutableLiveData和LiveData就行了
- 設計到數據聯動的時候考慮下MediatorLiveData
最后在說1個東西轉換
- 在項目中我們經常回運到數據轉換的問題比如int轉換成string或者int轉換成另一個對象
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主線程用沒問題,如果是子線程那就要用postValue
// userMutableLiveData.postValue(user);
if(Looper.myLooper() == Looper.getMainLooper()){
userMutableLiveData.setValue(user);
}else {
userMutableLiveData.postValue(user);
}
}
}
- 這時直接去監聽age就行了,只要userMutableLiveData的值有改變我們就能立馬監聽到User類中的年齡了
model.age.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Log.e("MainActivity",integer+"--");
}
});
switchMap
- switchMap和map同樣屬于數據轉換的一種,在實際項目中switchMap使用的頻率回比map更高map是通過監聽一個對象的變換然后進行轉換的,假設有另外一個場景通過用戶id進行網絡請求去拿到這個用戶的信息,按著map的寫法豈不是要有好幾個步驟先通過網絡請求去拿到user對象、再去賦值給LiveData、在轉換給LiveData、在對LiveData進行監聽改變ui。
- 下面直接模擬下子線程查詢進行用戶查詢
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("錢六",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("沒有該用戶",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;
}
}
- 監聽
model.userLiveData.observe(this, new Observer<User>() {
@Override
public void onChanged(User user) {
dataBinding.name.setText("名字:"+user.getName());
dataBinding.tvAge.setText("年齡:"+user.getAge());
}
});
- 當調用了queryUser方法就會去觸發switchMap方法中的Function接口apply方法去查詢用戶數據了
最后的最后說下onChanged回調是在主線程的,不然會拋異常這也解釋了子線程得用postValue去更新數值
-
setValue
image.png
image.png -
postValue
image.png
image.png