Android LiveData组件详解以及LiveDataBus
轉載請標明出處:https://blog.csdn.net/zhaoyanjun6/article/details/99749323
本文出自【趙彥軍的博客】
一、LiveData簡介
LiveData 是一個可以被觀察的數據持有類,它可以感知 Activity、Fragment或 Service 等組件的生命周期。簡單來說,他主要有一下優點。
- 它可以做到在組件處于激活狀態的時候才會回調相應的方法,從而刷新相應的 UI,不用擔心發生內存泄漏
- 當 config 導致 activity 重新創建的時候,不需要手動取處理數據的儲存和恢復。它已經幫我們封裝好了。
- 當 Actiivty 不是處于激活狀態的時候,如果你想 livedata setValue 之后立即回調 obsever 的 onChange 方法,而不是等到 Activity 處于激活狀態的時候才回調 obsever 的 onChange 方法,你可以使用 observeForever 方法,但是你必須在 onDestroy 的時候 removeObserver。
回想一下,在你的項目中,是不是經常會碰到這樣的問題,當網絡請求結果回來的時候,你經常需要判斷 Activity 或者 Fragment 是否已經 Destroy, 如果不是 destroy,才更新 UI。而當你如果使用 Livedata 的話,因為它是在 Activity 處于 onStart 或者 onResume 的狀態時,他才會進行相應的回調,因而可以很好得處理這個問題,不必謝一大堆的 activity.isDestroyed()。接下來,讓我們一起來看一下 LiveData 的使用。
二、使用
LiveData 是一個抽象類,它的實現子類有 MutableLiveData,MediatorLiveData。在實際使用中,用得比較多的MutableLiveData。他常常結合 ViewModel一起使用。下面,讓我們一起來看一下怎樣使用它?
import android.arch.lifecycle.MutableLiveData import android.arch.lifecycle.Observer import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.widget.Button import android.widget.TextViewclass MainActivity : AppCompatActivity() {lateinit var tv: TextViewvar liveData = MutableLiveData<String>() //定義liveDataprivate val changeObserver = Observer<String> { value ->value?.let { tv.text = it }}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)tv = findViewById(R.id.tv1)liveData.value = "123"liveData.observe(this, changeObserver) //注冊觀察者findViewById<Button>(R.id.cancel).setOnClickListener {liveData.value = "456"}} }注意事項
必須要從主線程調用 setValue(T) 方法來更新 LiveData 對象;如果代碼在工作線程中執行, 你可以使用 postValue(T) 方法來更新LiveData對象
三、LiveDataBus
3.1 為什么要用LiveDataBus替代EventBus和RxBus?
- LiveDataBus的實現及其簡單 相對 EventBus 復雜的實現,LiveDataBus 只需要一個類就可以實現。
- LiveDataBus可以減小APK包的大小 由于LiveDataBus只依賴 Android 官方 Android Architecture Components 組件的 LiveData ,沒有其他依賴,本身實現只有一個類。作為比較,EventBus JAR包大小為57kb,RxBus依賴RxJava和RxAndroid,其中RxJava2 包大小 2.2 MB,RxJava1 包大小 1.1 MB,RxAndroid 包大小 9 kb。使用 LiveDataBus 可以大大減小 APK 包的大小。
- LiveDataBus依賴方支持更好 LiveDataBus 只依賴 Android 官方 Android Architecture Components 組件的 LiveData ,相比 RxBus 依賴的 RxJava 和 RxAndroid,依賴方支持更好。
- LiveDataBus具有生命周期感知 LiveDataBus 具有生命周期感知,在 Android 系統中使用調用者不需要調用反注冊,相比 EventBus 和 RxBus 使用更為方便,并且沒有內存泄漏風險。
3.2 LiveDataBus的設計和架構
LiveDataBus的組成
- 消息 消息可以是任何的 Object,可以定義不同類型的消息,如 Boolean 、String 。也可以定義自定義類型的消息。
- 消息通道 LiveData 扮演了消息通道的角色,不同的消息通道用不同的名字區分,名字是 String 類型的,可以通過名字獲取到一個LiveData 消息通道。
- 消息總線 消息總線通過單例實現,不同的消息通道存放在一個 HashMap 中。
- 訂閱 訂閱者通過 getChannel 獲取消息通道,然后調用 observe 訂閱這個通道的消息。
- 發布 發布者通過 getChannel 獲取消息通道,然后調用 setValue 或者 postValue 發布消息。
3.3 LiveDataBus原理圖
3.4 LiveDataBus的實現
3.4.1 第一個實現
public final class LiveDataBus {private final Map<String, MutableLiveData<Object>> bus;private LiveDataBus() {bus = new HashMap<>();}private static class SingletonHolder {private static final LiveDataBus DATA_BUS = new LiveDataBus();}public static LiveDataBus get() {return SingletonHolder.DATA_BUS;}public <T> MutableLiveData<T> getChannel(String target, Class<T> type) {if (!bus.containsKey(target)) {bus.put(target, new MutableLiveData<>());}return (MutableLiveData<T>) bus.get(target);}public MutableLiveData<Object> getChannel(String target) {return getChannel(target, Object.class);} }短短二十行代碼,就實現了一個通信總線的全部功能,并且還具有生命周期感知功能,并且使用起來也及其簡單:
注冊訂閱:
LiveDataBus.get().getChannel("key_test", Boolean.class).observe(this, new Observer<Boolean>() {@Overridepublic void onChanged(@Nullable Boolean aBoolean) {}});發送消息:
LiveDataBus.get().getChannel("key_test").setValue(true);我們發送了一個名為”key_test”,值為true的事件。
這個時候訂閱者就會收到消息,并作相應的處理,非常簡單。
問題出現
對于 LiveDataBus 的第一版實現,我們發現,在使用這個 LiveDataBus 的過程中,訂閱者會收到訂閱之前發布的消息。對于一個消息總線來說,這是不可接受的。無論 EventBus 或者 RxBus,訂閱方都不會收到訂閱之前發出的消息。對于一個消息總線, LiveDataBus 必須要解決這個問題。
問題原因總結
對于這個問題,總結一下發生的核心原因。對于 LiveData,其初始的 version是-1,當我們調用了其 setValue 或者 postValue ,其 vesion 會+1;對于每一個觀察者的封裝 ObserverWrapper,其初始 version 也為-1,也就是說,每一個新注冊的觀察者,其version 為-1;當LiveData設置這個 ObserverWrapper 的時候,如果 LiveData 的 version 大于 ObserverWrapper的version,LiveData 就會強制把當前 value 推送給 Observer。
如何解決這個問題
明白了問題產生的原因之后,我們來看看怎么才能解決這個問題。很顯然,根據之前的分析,只需要在注冊一個新的訂閱者的時候把 Wrapper 的 version 設置成跟 LiveData 的 version 一致即可。
那么怎么實現呢,看看 LiveData的observe 方法,他會在步驟1創建一個 LifecycleBoundObserver,LifecycleBoundObserver 是ObserverWrapper 的派生類。然后會在步驟 2 把這個 LifecycleBoundObserver 放入一個私有 Map 容器 mObservers 中。無論ObserverWrapper 還是 LifecycleBoundObserver 都是私有的或者包可見的,所以無法通過繼承的方式更改 LifecycleBoundObserver 的 version。
那么能不能從 Map 容器 mObservers 中取到 LifecycleBoundObserver ,然后再更改 version 呢?答案是肯定的,通過查看SafeIterableMap 的源碼我們發現有一個 protected 的 get方法。因此,在調用 observe 的時候,我們可以通過反射拿到LifecycleBoundObserver,再把 LifecycleBoundObserver 的 version 設置成和 LiveData 一致即可。
LiveDataBus最終實現
public final class LiveDataBus {private final Map<String, BusMutableLiveData<Object>> bus;private LiveDataBus() {bus = new HashMap<>();}private static class SingletonHolder {private static final LiveDataBus DEFAULT_BUS = new LiveDataBus();}public static LiveDataBus get() {return SingletonHolder.DEFAULT_BUS;}public <T> MutableLiveData<T> with(String key, Class<T> type) {if (!bus.containsKey(key)) {bus.put(key, new BusMutableLiveData<>());}return (MutableLiveData<T>) bus.get(key);}public MutableLiveData<Object> with(String key) {return with(key, Object.class);}private static class ObserverWrapper<T> implements Observer<T> {private Observer<T> observer;public ObserverWrapper(Observer<T> observer) {this.observer = observer;}@Overridepublic void onChanged(@Nullable T t) {if (observer != null) {if (isCallOnObserve()) {return;}observer.onChanged(t);}}private boolean isCallOnObserve() {StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();if (stackTrace != null && stackTrace.length > 0) {for (StackTraceElement element : stackTrace) {if ("android.arch.lifecycle.LiveData".equals(element.getClassName()) &&"observeForever".equals(element.getMethodName())) {return true;}}}return false;}}private static class BusMutableLiveData<T> extends MutableLiveData<T> {private Map<Observer, Observer> observerMap = new HashMap<>();@Overridepublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {super.observe(owner, observer);try {hook(observer);} catch (Exception e) {e.printStackTrace();}}@Overridepublic void observeForever(@NonNull Observer<T> observer) {if (!observerMap.containsKey(observer)) {observerMap.put(observer, new ObserverWrapper(observer));}super.observeForever(observerMap.get(observer));}@Overridepublic void removeObserver(@NonNull Observer<T> observer) {Observer realObserver = null;if (observerMap.containsKey(observer)) {realObserver = observerMap.remove(observer);} else {realObserver = observer;}super.removeObserver(realObserver);}private void hook(@NonNull Observer<T> observer) throws Exception {//get wrapper's versionClass<LiveData> classLiveData = LiveData.class;Field fieldObservers = classLiveData.getDeclaredField("mObservers");fieldObservers.setAccessible(true);Object objectObservers = fieldObservers.get(this);Class<?> classObservers = objectObservers.getClass();Method methodGet = classObservers.getDeclaredMethod("get", Object.class);methodGet.setAccessible(true);Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);Object objectWrapper = null;if (objectWrapperEntry instanceof Map.Entry) {objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();}if (objectWrapper == null) {throw new NullPointerException("Wrapper can not be bull!");}Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");fieldLastVersion.setAccessible(true);//get livedata's versionField fieldVersion = classLiveData.getDeclaredField("mVersion");fieldVersion.setAccessible(true);Object objectVersion = fieldVersion.get(this);//set wrapper's versionfieldLastVersion.set(objectWrapper, objectVersion);}} }注冊訂閱
LiveDataBus.get().with("key_test", String.class).observe(this, new Observer<String>() {@Overridepublic void onChanged(@Nullable String s) {}});發送消息
LiveDataBus.get().with("key_test").setValue(s);源碼說明
LiveDataBus 的源碼可以直接拷貝使用,也可以前往作者的 GitHub 倉庫查看下載:
https://github.com/JeremyLiao/LiveDataBus 。
參考資料
Android LiveData 使用詳解
Android消息總線的演進之路:用LiveDataBus替代RxBus、EventBus
總結
以上是生活随笔為你收集整理的Android LiveData组件详解以及LiveDataBus的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Lifecycle 生命
- 下一篇: Android输出签名的 SHA1 值