本文介紹:LiveData的單獨使用、LiveData與ViewModel結合使用、LiveData的複雜使用
一、LiveData的單獨使用
以一個整型數據爲例。將該整型數據用MutableLiveData包裝。
在Activity中,監聽count的變化:每次點擊按鈕,會生成隨機數,並Toast表現。
public class LiveDataActivity extends AppCompatActivity {
//定義一個MutableLiveData包裝的Integer型數據count
private MutableLiveData<Integer> count = new MutableLiveData<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_data);
//監聽count的變化
count.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
//展示數據的變化
Toast.makeText(LiveDataActivity.this, String.valueOf(integer), Toast.LENGTH_SHORT).show();
}
});
findViewById(R.id.liveDataOnlyTest).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//驅動數據變化
count.setValue(new Random().nextInt());
}
});
}
}
該場景也可以擴展爲:監聽全局變量的數據變化,如Application中定義的變量等
二、LiveData結合ViewModel使用
ViewModel作爲頁面數據的存儲空間。給相應的頁面定義相應的ViewModel,管理該頁面的所有數據。
2-0:定義一個ViewModel
public class MainViewModel extends ViewModel {
private MutableLiveData<Integer> count = new MutableLiveData<>();
public MutableLiveData<Integer> getCount() {
return count;
}
}
2-1:在頁面上使用該ViewModel
頁面上的ViewModel數據監聽
//獲取ViewModel
mainViewModel = new ViewModelProvider(this).get(MainViewModel.class);
//監聽ViewModel中的count數據變化
mainViewModel.getCount().observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Toast.makeText(LiveDataActivity.this, "ViewModel+LiveData示例數據變化:" + integer, Toast.LENGTH_SHORT).show();
}
});
......
//驅動數據變化
findViewById(R.id.liveDataWithViewModel).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mainViewModel.getCount().setValue(new Random().nextInt());
}
});
三、LiveData的使用進階
3-0:LiveData數據轉型---map
用於將該LiveData中的數據做一次過濾或者轉變,生成一個新的LiveData。以一個Person類作爲此次例子,該類含有name與age兩個屬性。將LiveData<Person>用Map轉換後,只輸出LiveData<String>。
//類定義
public class Person {
public String name;
public int age;
}
//ViewModel定義
public class PersonViewModel extends ViewModel {
private MutableLiveData<Person> person = new MutableLiveData<>();
public MutableLiveData<Person> getPerson() {
return person;
}
}
------------以下爲頁面數據-----------
//ViewModel獲取
personViewModel = new ViewModelProvider(this).get(PersonViewModel.class);
//數據轉換的規則定義:將Person類變爲只返回該類中的name
LiveData<String> map = Transformations.map(personViewModel.getPerson(), new Function<Person, String>() {
@Override
public String apply(Person input) {
return input.name;
}
});
//監聽數據變化
map.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Toast.makeText(LiveDataActivity.this, "Person過濾轉型後得到的名字:" + s, Toast.LENGTH_SHORT).show();
}
});
//驅動數據變化
findViewById(R.id.liveDataWithViewModelWhemMap).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Person person = new Person();
person.name = "曉明";
person.age = 18;
personViewModel.getPerson().setValue(person);
}
});
3-1:LiveData的數據轉型---switchMap
//ViewModel定義
personViewModel = new ViewModelProvider(this).get(PersonViewModel.class);
//switchMap轉換,與map不同的是,該方法的apply需要返回一個新的LiveData格式的數據。
LiveData<String> stringLiveData = Transformations.switchMap(personViewModel.getPerson(), new Function<Person, LiveData<String>>() {
@Override
public LiveData<String> apply(Person input) {
MutableLiveData test = new MutableLiveData<>();
test.setValue(input.name);
return test;
}
});
//數據變化監聽
stringLiveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Toast.makeText(LiveDataActivity.this, "Person經SwitchMap轉型後得到的名字:" + s, Toast.LENGTH_SHORT).show();
}
});
//驅動數據變化
findViewById(R.id.liveDataWithViewModelWhemSwitchMap).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Person person = new Person();
person.name = "曉紅";
person.age = 18;
personViewModel.getPerson().setValue(person);
}
});
switchMap方法中,apply返回的是一個LiveData類型的數據。這主要是在這樣的場景中使用的:ViewModel中定義的LiveData,命名爲A。A的數據不是在ViewModel中直接組裝的,而是要通過調用其他方法獲取的。此時,用switchMap將獲取到的LiveData,賦值給 A。
常見的場景有:ViewModel中調用方法從網絡或者數據庫獲取數據。
3-2:MediatorLiveData的使用
MediatorLiveData是LiveData的子類,可以合併多個LiveData數據源,當任意一個數據源數據發生變化,MediatorLiveData的監聽都將起效。
//source1與source2作爲兩個數據源
private MutableLiveData<Integer> source1 = new MutableLiveData<>();
private MutableLiveData<Integer> source2 = new MutableLiveData<>();
//source整合兩個數據源
public MediatorLiveData<Integer> source = new MediatorLiveData<>();
//統一的監聽回調
private Observer<Integer> observer = new Observer<Integer>() {
@Override
public void onChanged(Integer s) {
source.setValue(s);
}
};
-----------------頁面-----------------
//添加數據源
source.addSource(source1,observer);
source.addSource(source2,observer);
source.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Toast.makeText(LiveDataActivity.this, "--->"+integer, Toast.LENGTH_SHORT).show();
}
});
----------------驅動數據--------------
//無論是source1還是source2的數據變化,都將觸發回調
source1.postValue(1);
source2.setValue(2);
MediatorLiveData可用於網絡與本地數據庫同時存在,有兩個數據源時,無論二者中,哪部分數據發生變化,都能起效。
3-3:LiveData的封裝---LiveDataBus
應用中,存在多個頁面之間,都會共用一個或者一組數據的情況。
1、如ActivityA,ActivityB都會同時監聽某個數據的變化,需要根據這個數據的變化,做某些操作。
2、組件之間同時監聽同一數據的變化。
3、組件間的通訊。
以上幾種情況都可以通過封裝LiveData,實現一個LiveData的通訊框架來實現。命名這個框架爲LiveDataBus。
這個框架同時也會解決前文:JetPack架構---LiveData特性、原理 中提到的“爲什麼LiveData會帶有粘性特製”,把該特性屏蔽,避免額外問題。
LiveDataBus的實現只需一個類,如下(部分引用自網絡),該類簡單的說,就是一個Map,用於存儲多個LiveData。
public class LiveDataBus {
private static volatile LiveDataBus INSTANCE;
private final Map<String, MutableLiveData<Object>> bus;
private LiveDataBus() {
bus = new HashMap<>();
}
public static LiveDataBus get(){
if(INSTANCE == null){
synchronized (LiveDataBus.class){
INSTANCE = new LiveDataBus();
}
}
return INSTANCE;
}
//此處type用於後續可能的observe或者post/set value
//此處key用於生成或者獲取,該key對應的LiveData
public synchronized <T> MutableLiveData<T> with(String key,Class<T> type){
if (!bus.containsKey(key)){
bus.put(key,new BusMutableLiveData<>());
}
return (MutableLiveData<T>) bus.get(key);
}
//hook會反射方式複寫 observer中version的控制
public static class BusMutableLiveData<T> extends MutableLiveData<T> {
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
super.observe(owner, observer);
try {
hook(observer);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 反射技術 使observer.mLastVersion = mVersion
*
* @param observer ob
*/
private void hook(Observer<? super T> observer) throws Exception {
//根據源碼 如果使observer.mLastVersion = mVersion; 就不會走 回調OnChange方法了,所以就算註冊
//也不會收到消息
//首先獲取liveData的class
Class<LiveData> classLiveData = LiveData.class;
//通過反射獲取該類裏mObserver屬性對象
Field fieldObservers = classLiveData.getDeclaredField("mObservers");
//設置屬性可以被訪問
fieldObservers.setAccessible(true);
//獲取的對象是this裏這個對象值,他的值是一個map集合
Object objectObservers = fieldObservers.get(this);
//獲取map對象的類型
Class<?> classObservers = objectObservers.getClass();
//獲取map對象中所有的get方法
Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
//設置get方法可以被訪問
methodGet.setAccessible(true);
//執行該get方法,傳入objectObservers對象,然後傳入observer作爲key的值
Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
//定義一個空的object對象
Object objectWrapper = null;
//判斷objectWrapperEntry是否爲Map.Entry類型
if (objectWrapperEntry instanceof Map.Entry) {
objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
}
if (objectWrapper == null) {
throw new NullPointerException("Wrapper can not be null!");
}
//如果不是空 就得到該object的父類
Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();
//通過他的父類的class對象,獲取mLastVersion字段
Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
fieldLastVersion.setAccessible(true);
Field fieldVersion = classLiveData.getDeclaredField("mVersion");
fieldVersion.setAccessible(true);
Object objectVersion = fieldVersion.get(this);
//把mVersion 字段的屬性值設置給mLastVersion
fieldLastVersion.set(objectWrapper, objectVersion);
}
}
}
LiveDataBus的使用方式很簡便:
//LiveData監聽,該LiveData的識別碼是Define.STR_KEY,該值隨意定義
LiveDataBus.get().with(Define.STR_KEY, String.class).observe(Main2Activity.this, new Observer<String>() {
@Override
public void onChanged(String info) {
Log.e(TAG, "onChanged: " + info);
}
});
//LiveData,驅動數據變化
LiveDataBus.get().with(Define.INFO_KEY, String.class).postValue(info);
LiveData從入門用法、與ViewModel結合,到後續的轉換、封裝。基本囊括了目前LiveData的使用方式。如需擴展等,仍然可以根據LiveData的特性,自定義相應的LiveData。