組件間通信方案(六):自動感知生命周期事件總線LiveDataBus

一、LiveData

LiveData是17年GoogleIO大會上提出來的一個新技術(shù)。相對于通信總線類型的框架EventBus和RxBus來說,它更簡單,更簡潔、更解耦。

它具有以下優(yōu)點:
UI和實時數(shù)據(jù)保持一致
因為LiveData采用的是觀察者模式,這樣一來就可以再數(shù)據(jù)發(fā)生改變時獲得通知,更新UI

避免內(nèi)存泄漏
觀察者被綁定到組件的生命周期上,當(dāng)被綁定的組件銷毀(Destory)時,觀察者會立刻自動清理自身的數(shù)據(jù)。

不會再產(chǎn)生由于Activity處于stop狀態(tài)而引起的崩潰
當(dāng)Activity處于后臺狀態(tài)時,是不會收到LiveData的任何事件的

不需要再解決生命周期帶來的問題
LiveData可以感知被綁定的組件的生命周期,只有在活躍狀態(tài)才會通知數(shù)據(jù)變化

實時數(shù)據(jù)刷新
當(dāng)組件處于活躍狀態(tài)或者從不活躍狀態(tài)到活躍狀態(tài)時總是能收到最新的數(shù)據(jù)

解決Confinguration Change問題
在屏幕發(fā)生旋轉(zhuǎn)或者被回收再次啟動,立刻就能收到最新的數(shù)據(jù)

二、簡要分析運用方式:

當(dāng) LiveData 所持有的數(shù)據(jù)改變時,它會通知相應(yīng)的界面代碼進(jìn)行更新。
同時,LiveData 持有界面代碼 Lifecycle (生命周期組件)的引用,這意味著它會在界面代碼(LifecycleOwner)的生命周期處于 started 或 resumed 時作出相應(yīng)更新,而在 LifecycleOwner 被銷毀時停止更新。
另外ViewModel的優(yōu)點也很明顯,為Activity 、Fragment存儲數(shù)據(jù),直到完全銷毀。尤其是屏幕旋轉(zhuǎn)的場景,常用的方法都是通過onSaveInstanceState()保存數(shù)據(jù),再在onCreate()中恢復(fù)。

既然LiveData 在線程中傳遞事件這么優(yōu)秀,那么我們應(yīng)用到項目總的話,就可以集成到一塊。這樣就誕生了LiveDataBus。

三、LiveDataBus優(yōu)點:

LiveDataBus的實現(xiàn)及其簡單
相對EventBus復(fù)雜的實現(xiàn),LiveDataBus只需要一個類就可以實現(xiàn)

LiveDataBus可以減小APK包的大小
LiveDataBus只依賴Android官方組件LiveData,本身實現(xiàn)只一個類。EventBus 57Kb、RxJava 2.2M

LiveDataBus 依賴方支持更好
LiveDataBus只依賴Android官方組件LiveData,相比RxBus依賴的RxJava和RxAndroid,依賴方支持更好

LiveDataBus具有生命周期感知
LiveDataBus具有生命周期感知,在Android系統(tǒng)中使用調(diào)用者不需要調(diào)用反注冊,相比EventBus和RxBus使用更為方便,并且沒有內(nèi)存泄漏風(fēng)險。

四、代碼展示:

以上我們已經(jīng)知道了,LiveData訂閱消息和發(fā)送消息的方式。
那么應(yīng)用到項目中時,使用Map將這些LiveData保存記錄下來,這樣各個頁面的調(diào)用存取都可以寫到同一個Map中,就形成總線機制。

訂閱消息
1、observe 生命周期感知,不需要手動取消訂閱

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

2、observeForever 需要手動取消訂閱

LiveDataBus.get().with("key_name", String.class).observeForever(observer);
LiveDataBus.get().with("key_name", String.class).removeObserver(observer);

發(fā)送消息
1、setValue 在主線程發(fā)送消息

LiveDataBus.get().with("key_name").setValue(value);

2、postValue 在后臺線程發(fā)送消息,訂閱者會在主線程收到消息

LiveDataBus.get().with("key_name").postValue(value);

Sticky模式
支持在注冊訂閱者的時候設(shè)置Sticky模式,這樣訂閱者可以接收到訂閱之前發(fā)送的消息

observeSticky 生命周期感知,不需要手動取消訂閱,Sticky模式

LiveDataBus.get()
        .with("sticky_key", String.class)
        .observeSticky(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {

            }
        });

observeStickyForever 需要手動取消訂閱,Sticky模式

LiveDataBus.get().with("sticky_key", String.class).observeStickyForever(observer);
LiveDataBus.get().with("sticky_key", String.class).removeObserver(observer);
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 synchronized <T> BusMutableLiveData<T> with(String key, Class<T> type) {
        if (!bus.containsKey(key)) {
            bus.put(key, new BusMutableLiveData<>());
        }
        return (BusMutableLiveData<T>) bus.get(key);
    }

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

    public static class BusMutableLiveData<T> extends MutableLiveData<T> {



        private class PostValueTask implements Runnable {
            private Object newValue;

            public PostValueTask(@NonNull Object newValue) {
                this.newValue = newValue;
            }

            @Override
            public void run() {
                setValue((T) newValue);
            }
        }

        private Map<Observer, Observer> observerMap = new HashMap<>();
        private Handler mainHandler = new Handler(Looper.getMainLooper());

        @Override
        public void postValue(T value) {
            mainHandler.post(new PostValueTask(value));
        }

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

        public void observeSticky(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
            super.observe(owner, observer);
        }

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

        public void observeStickyForever(@NonNull Observer<T> observer) {
            super.observeForever(observer);
        }

        @Override
        public 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 version
            Class<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 version
            Field fieldVersion = classLiveData.getDeclaredField("mVersion");
            fieldVersion.setAccessible(true);
            Object objectVersion = fieldVersion.get(this);
            //set wrapper's version
            fieldLastVersion.set(objectWrapper, objectVersion);
        }
    }
}

注:
在BusMutableLiveData類中,hook函數(shù)利用反射機制重新給wrapper的version賦值了。跟蹤源碼可知:這么操作的意義是為了在Activity未創(chuàng)建時,不接收發(fā)布消息者發(fā)布過來的消息。(例:網(wǎng)絡(luò)請求后使用LiveData向AActivity發(fā)送消息,但是AActivity還未創(chuàng)建,這樣一旦AActivity創(chuàng)建后onResume函數(shù)調(diào)用,View會跟著數(shù)據(jù)變化。但如果我不想AActivity創(chuàng)建后View有所改變呢,就使用調(diào)用hook函數(shù)的observe去訂閱就可以了)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容