元件之間的通訊LiveDataBus

昊霖Lee發表於2019-05-11

關於LiveDataBus

原始碼地址 GitHub

  ① LiveData是Android Architecture Components提出的框架。LiveData是一個可以被觀察的資料持有類,它可以感知並遵循
     Activity、Fragment或Service等元件的生命週期。正是由於LiveData對元件生命週期可感知特點,因此可以做到僅在元件處
     於生命週期的啟用狀態時才更新UI資料。

  ② LiveData需要一個觀察者物件,一般是Observer類的具體實現。當觀察者的生命週期處於STARTED或RESUMED狀態時,LiveData會通知
     觀察者資料變化;在觀察者處於其他狀態時,即使LiveData的資料變化了,也不會通知。

複製程式碼

LiveData的優點


  ① UI和實時資料保持一致,因為LiveData採用的是觀察者模式,這樣一來就可以在資料發生改變時獲得通知,更新UI。

  ② 避免記憶體洩漏,觀察者被繫結到元件的生命週期上,當被繫結的元件銷燬(destroy)時,觀察者會立刻自動清理自身的資料。

  ③ 不會再產生由於Activity處於stop狀態而引起的崩潰,例如:當Activity處於後臺狀態時,是不會收到LiveData的任何事件的。

  ④ 不需要再解決生命週期帶來的問題,LiveData可以感知被繫結的元件的生命週期,只有在活躍狀態才會通知資料變化。

  ⑤ 實時資料重新整理,當元件處於活躍狀態或者從不活躍狀態到活躍狀態時總是能收到最新的資料。

  ⑥ 解決Configuration Change問題,在螢幕發生旋轉或者被回收再次啟動,立刻就能收到最新的資料。
複製程式碼

為什麼要用LiveDataBus替代EventBus和RxBus


  ① LiveDataBus的實現及其簡單,相對EventBus複雜的實現,LiveDataBus只需要一個類就可以實現。

  ② LiveDataBus可以減小APK包的大小,由於LiveDataBus只依賴Android官方Android Architecture Components元件的LiveData,沒
     有其他依賴,本身實現只有一個類。作為比較,EventBus JAR包大小為57kb,RxBus依賴RxJava和RxAndroid,其中RxJava2包大小
     2.2MB,RxJava1包大小1.1MB,RxAndroid包大小9kb。使用LiveDataBus可以大大減小APK包的大小。

  ③ LiveDataBus依賴方支援更好,LiveDataBus只依賴Android官方Android Architecture Components元件的LiveData,相比RxBus
     依賴的RxJava和RxAndroid,依賴方支援更好。

  ④ LiveDataBus具有生命週期感知,LiveDataBus具有生命週期感知,在Android系統中使用呼叫者不需要呼叫反註冊,相比
     EventBus和RxBus使用更為方便,並且沒有記憶體洩漏風險。

複製程式碼

LiveDataBus的組成

  ① 訊息
     訊息可以是任何的Object,可以定義不同型別的訊息,如Boolean、String。也可以定義自定義型別的訊息。

  ② 訊息通道
     LiveData扮演了訊息通道的角色,不同的訊息通道用不同的名字區分,名字是String型別的,可以通過名字獲取到一個LiveData訊息
     通道。

  ③ 訊息匯流排
     訊息匯流排通過單例實現,不同的訊息通道存放在一個HashMap中。

  ④ 訂閱 Observer
     訂閱者通過getChannel獲取訊息通道,然後呼叫observe訂閱這個通道的訊息。

  ⑤ 釋出 setValue postValue
     釋出者通過getChannel獲取訊息通道,然後呼叫setValue或者postValue釋出訊息。
複製程式碼
  • 訂閱註冊
    LiveDataBus.get().with("MainActivity", HuaWei.class).observe(this, new Observer<HuaWei>() {
              @Override
              public void onChanged(@Nullable HuaWei huaWei) {
                  if (huaWei != null)
                      Toast.makeText(MainActivity.this, huaWei.getName(), Toast.LENGTH_SHORT).show();
              }
          });
複製程式碼
  • 傳送訊息
        HuaWei huaWei = new HuaWei("華為","P30Pro");
        LiveDataBus.get().with("MainActivity",HuaWei.class).postValue(huaWei);
複製程式碼

LiveDataBus原理圖

元件之間的通訊LiveDataBus

LiveDataBus 問題出現

  • 對於LiveDataBus的第一版實現,我們發現,在使用這個LiveDataBus的過程中,訂閱者會收到訂閱之前釋出的訊息。對於一個 訊息匯流排來說,這是不可接受的。無論EventBus或者RxBus,訂閱方都不會收到訂閱之前發出的訊息。對於一個訊息匯流排, LiveDataBus必須要解決這個問題。

LiveDataBus 問題原因總結

  • 對於這個問題,總結一下發生的核心原因。對於LiveData,其初始的version是-1,當我們呼叫了其setValue或者postValue, 其vesion會+1;對於每一個觀察者的封裝ObserverWrapper,其初始version也為-1,也就是說,每一個新註冊的觀察者,其 version為-1;當LiveData設定這個ObserverWrapper的時候,如果LiveData的version大於ObserverWrapper的version, LiveData就會強制把當前value推送給Observer

LiveDataBus 最終實現

  • LiveDataBus 實現

        public final class LiveDataBus {
        
            private final Map<String, MutableLiveData<Object>> bus;
        
            private LiveDataBus() {
                bus = new HashMap<>();
            }
        
            private static class SingletonHolder {
                private static final LiveDataBus LIVE_DATA_BUS = new LiveDataBus();
            }
        
            public static LiveDataBus get() {
                return SingletonHolder.LIVE_DATA_BUS;
            }
            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);
            }
        }

複製程式碼
  • LiveDataBus 反射 使observer.mLastVersion = mVersion
        public class BusMutableLiveData<T> extends MutableLiveData<T> {
            @Override
            public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
                super.observe(owner, observer);
                try {
                    hook(observer);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        
            /**
             * 反射技術  使observer.mLastVersion = mVersion
             *
             * @param observer ob
             */
            private void hook(Observer<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);
            }
        }
        
複製程式碼

相關文章