天天看點

Android Jetpack-LiveDataBus分析Android Architecture ComponentsLiveDataLiveDataBusLiveDataBus的設計和架構總結

文章目錄

  • Android Architecture Components
    • Android Architecture Components的特點
  • LiveData
    • LiveData的優點
  • LiveDataBus
    • 使用LiveData的理由
    • 為什麼要用LiveDataBus替代EventBus和RxBus
  • LiveDataBus的設計和架構
    • LiveDataBus的組成
    • LiveDataBus原理圖
    • LiveDataBus的實作
      • 第一個實作
      • 問題出現
      • 問題分析
      • 問題原因總結
      • 如何解決這個問題
      • LiveDataBus最終實作
  • 總結

參考:

作者的GitHub倉庫:

https://github.com/JeremyLiao/LiveEventBus

https://www.cnblogs.com/meituantech/p/9376449.html

Android Architecture Components

Android Architecture Components的核心是Lifecycle、LiveData、ViewModel 以及 Room,通過它可以非常優雅的讓資料與界面進行互動,并做一些持久化的操作,高度解耦,自動管理生命周期,而且不用擔心記憶體洩漏的問題。

  • Room

    一個強大的SQLite對象映射庫。

  • ViewModel

    一類對象,它用于為UI元件提供資料,在裝置配置發生變更時依舊可以存活。

  • LiveData 一個可感覺生命周期、可被觀察的資料容器,它可以存儲資料,還會在資料發生改變時進行提醒。
  • Lifecycle

    包含LifeCycleOwer和LifecycleObserver,分别是生命周期所有者和生命周期感覺者。

Android Architecture Components的特點

  • 資料驅動型程式設計

    變化的永遠是資料,界面無需更改。

  • 感覺生命周期,防止記憶體洩漏
  • 高度解耦

    資料,界面高度分離。

  • 資料持久化

    資料、ViewModel不與 UI的生命周期挂鈎,不會因為界面的重建而銷毀。

LiveData

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

重點:為什麼使用LiveData建構資料通信總線LiveDataBus

使用LiveData的理由

  • LiveData具有的這種可觀察性和生命周期感覺的能力,使其非常适合作為Android通信總線的基礎構件。
  • 使用者不用顯示調用反注冊方法。

    由于LiveData具有生命周期感覺能力,是以LiveDataBus隻需要調用注冊回調方法,而不需要顯示的調用反注冊方法。這樣帶來的好處不僅可以編寫更少的代碼,而且可以完全杜絕其他通信總線類架構(如EventBus、RxBus)忘記調用反注冊所帶來的記憶體洩漏的風險。

為什麼要用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的設計和架構

LiveDataBus的組成

  1. 消息

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

  2. 消息通道

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

  3. 消息總線

    消息總線通過單例實作,不同的消息通道存放在一個HashMap中。

  4. 訂閱

    訂閱者通過getChannel擷取消息通道,然後調用observe訂閱這個通道的消息。

  5. 釋出

    釋出者通過getChannel擷取消息通道,然後調用setValue或者postValue釋出消息。

LiveDataBus原理圖

Android Jetpack-LiveDataBus分析Android Architecture ComponentsLiveDataLiveDataBusLiveDataBus的設計和架構總結

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 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>() {
                    @Override
                    public void onChanged(@Nullable Boolean aBoolean) {
                        
                    }
                });
           

發送消息:

我們發送了一個名為"key_test",值為true的事件。

這個時候訂閱者就會收到消息,并作相應的處理,非常簡單。

問題出現

在MainActivity中先發送消息

在SecActivity中訂閱消息

LiveDataBus.get().getChannel("key_test", Boolean.class)
                .observe(this, new Observer<Boolean>() {
                    @Override
                    public void onChanged(@Nullable Boolean aBoolean) {
                        textview.setText(aBoolean + "");
                    }
                });
           

訂閱消息 發生在 發送消息之後,但此時依然能接收到消息。

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

問題分析

打開LiveData.java,找到Observer的onChanged方法被調用的地方,如下:

Android Jetpack-LiveDataBus分析Android Architecture ComponentsLiveDataLiveDataBusLiveDataBus的設計和架構總結

隻要讓onChanged方法不被調用,那麼SecActivity就不會接受消息。

如何讓onChanged方法有一次不被執行呢?

從上面代碼可以看到,onChanged()方法被執行前,有三個return可能會被執行,隻要執行任意一個return,那麼onChanged()方法就不會被執行。

當LifeCircleOwner的狀态發生變化的時候,會調用LiveData.ObserverWrapper的activeStateChanged函數,如果這個時候ObserverWrapper的狀态是active,就會調用LiveData的dispatchingValue。

Android Jetpack-LiveDataBus分析Android Architecture ComponentsLiveDataLiveDataBusLiveDataBus的設計和架構總結

在LiveData的dispatchingValue中,又會調用LiveData的considerNotify方法。

Android Jetpack-LiveDataBus分析Android Architecture ComponentsLiveDataLiveDataBusLiveDataBus的設計和架構總結

在LiveData的considerNotify方法中,紅框中的邏輯是關鍵,如果ObserverWrapper的mLastVersion小于LiveData的mVersion,就會去回調mObserver的onChanged方法。而每個新的訂閱者,其version都是-1,LiveData一旦設定過其version是大于-1的(每次LiveData設定值都會使其version加1),這樣就會導緻LiveDataBus每注冊一個新的訂閱者,這個訂閱者立刻會收到一個回調,即使這個設定的動作發生在訂閱之前。

Android Jetpack-LiveDataBus分析Android Architecture ComponentsLiveDataLiveDataBusLiveDataBus的設計和架構總結

問題原因總結

對于這個問題,總結一下發生的核心原因。對于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一緻即可。

Android Jetpack-LiveDataBus分析Android Architecture ComponentsLiveDataLiveDataBusLiveDataBus的設計和架構總結

對于非生命周期感覺的observeForever方法來說,實作的思路是一緻的,但是具體的實作略有不同。observeForever的時候,生成的wrapper不是LifecycleBoundObserver,而是AlwaysActiveObserver(步驟1),而且我們也沒有機會在observeForever調用完成之後再去更改AlwaysActiveObserver的version,因為在observeForever方法體内,步驟3的語句,回調就發生了。

Android Jetpack-LiveDataBus分析Android Architecture ComponentsLiveDataLiveDataBusLiveDataBus的設計和架構總結

那麼對于observeForever,如何解決這個問題呢?既然是在調用内回調的,那麼我們可以寫一個ObserverWrapper,把真正的回調給包裝起來。把ObserverWrapper傳給observeForever,那麼在回調的時候我們去檢查調用棧,如果回調是observeForever方法引起的,那麼就不回調真正的訂閱者。

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;
        }

        @Override
        public 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<>();


        @Override
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
            super.observe(owner, observer);
            try {
                hook(observer);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


        @Override
        public void observeForever(@NonNull Observer<? super T> observer) {
            if (!observerMap.containsKey(observer)) {
                observerMap.put(observer, new ObserverWrapper(observer));
            }
            super.observeForever(observerMap.get(observer));
        }

        @Override
        public void removeObserver(@NonNull Observer<? super T> observer) {
            Observer realObserver = null;
            if (observerMap.containsKey(observer)) {
                realObserver = observerMap.remove(observer);
            } else {
                realObserver = observer;
            }
            super.removeObserver(realObserver);
        }

        /**
         * 步驟:
         * 1.得到mLastVersion
         *
         * 2.得到mVersion
         *
         * 3.把mVersion的值填入到得到mLastVersion中
         */
        private void hook(@NonNull Observer<? super T> observer) throws Exception {
            //get wrapper's version
            //1.得到mLastVersion
            Class<LiveData> classLiveData = LiveData.class;
            Field fieldObservers = classLiveData.getDeclaredField("mObservers");
            fieldObservers.setAccessible(true);
            //擷取到這個成員變量對應的對象
            Object objectObservers = fieldObservers.get(this);
            //得到map
            Class<?> classObservers = objectObservers.getClass();
            //擷取到mObservers對象的get方法
            Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
            methodGet.setAccessible(true);
            //執行get方法
            Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
            //取到map中的value
            Object objectWrapper = null;
            if (objectWrapperEntry instanceof Map.Entry) {
                objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
            }
            if (objectWrapper == null) {
                throw new NullPointerException("Wrapper can not be bull!");
            }
            //得到ObserverWrapper的類對象
            Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();
            Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
            fieldLastVersion.setAccessible(true);

            //get livedata's version
            //2.得到mVersion
            Field fieldVersion = classLiveData.getDeclaredField("mVersion");
            fieldVersion.setAccessible(true);

            //3.把mVersion的值填入到mLastVersion中
            Object objectVersion = fieldVersion.get(this);
            //set wrapper's version
            fieldLastVersion.set(objectWrapper, objectVersion);
        }
    }
}
           

注冊訂閱:

LiveDataBus.get()
        .with("key_test", String.class)
        .observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
            }
        });
           

發送消息:

總結

消息總線架構LiveDataBus:訂閱者可以訂閱某個消息通道的消息,釋出者可以把消息釋出到消息通道上。利用LiveDataBus,不僅可以實作消息總線功能,而且對于訂閱者,他們不需要關心何時取消訂閱,極大減少了因為忘記取消訂閱造成的記憶體洩漏風險。