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 是一個抽象類,它的實現子類有 MutableLiveDataMediatorLiveData。在實際使用中,用得比較多的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.TextView

class MainActivity : AppCompatActivity() {

    lateinit var tv: TextView
    var liveData = MutableLiveData<String>() //定義liveData

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

        @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<T> observer) {
            super.observe(owner, observer);
            try {
                hook(observer);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

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

註冊訂閱

LiveDataBus.get()
        .with("key_test", String.class)
        .observe(this, new Observer<String>() {
            @Override
            public 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章