LiveData源碼解析

LiveData作爲jettpack的一環,常搭配ViewModel一起使用;相比於RxJava,用LiveData封裝的LiveDataBus更加簡潔方便,並且LiveData會感知頁面的信息變化,當頁面是不可見的時候,及時Data有數據更新,頁面也不會接收到,然後在頁面重新可見再去把數據通知給頁面,而且在頁面銷燬時也會及時銷燬數據,大大降低了內存泄露的發生;這篇主要簡單分析一下LiveData的流程

1.LiveData的使用

class TestActivity :AppCompactActivity(){ 

var statusData: MutableLiveData<TestLiveStatus> = MutableLiveData()

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    statusData.observe(this, this)
}

 override fun onChanged(t: TestLiveStatus?) {
        Log.e("TAG", "onChanged -> $t ");
    }
}

使用很方便,只需要註冊一個監聽,然後在監聽裏處理狀態變化就可以

2.LiveData源碼分析

2.1 livedata的註冊

@MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

LifeCycle是頁面的生命週期監聽,主要是通過在Activity裏面添加一個空的ReportFragment,監聽這個Fragment的狀態來實現;具體我會在下一篇中描述,這裏只需要知道這個可以感知Activity的生命週期即可

這裏先會創建一個LifeCycleBoundObserver ,並把owner和observer都傳進去,然後用mObserv

 class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @NonNull
        final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }

        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }

這裏主要關注兩個方法shouldBeActiveonStateChanged

shouldBeActivite個枚舉判斷,如果枚舉的類型比STARTED大則返回true

 public boolean isAtLeast(@NonNull State state) {
            return compareTo(state) >= 0;
        }

我們看一下這個State的聲明

public enum State {

    DESTROYED,
    INITIALIZED,
    CREATED,
    STARTED,
    RESUMED 

    public boolean isAtLeast(@NonNull State state) {
            return compareTo(state) >= 0;
        }
}

STARTED大的只有RESUME

LifeCycle和這個State的有個對應關係可以參考一下,所以我們可以認爲這個shouldBeActive返回的是表示是頁面是否是從不可見變成了可見狀態

 static State getStateAfter(Event event) {
        switch (event) {
            case ON_CREATE:
            case ON_STOP:
                return CREATED;
            case ON_START:
            case ON_PAUSE:
                return STARTED;
            case ON_RESUME:
                return RESUMED;
            case ON_DESTROY:
                return DESTROYED;
            case ON_ANY:
                break;
        }
        throw new IllegalArgumentException("Unexpected event value " + event);
    }

onStateChanged中包含兩部分

1.當頁面被銷燬時,註冊的觀察者被清除,後續的事件通知也隨之終止

2.當頁面非銷燬狀態時,處理相應的狀態變化邏輯

void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                onActive();
            }
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive();
            }
            if (mActive) {
                dispatchingValue(this);
            }
        }

可以看出,這個方法只處理活躍狀態變化的,而從上面的分析,可以認爲就是頁面可見性變化的處理

默認mActiveCount = 0 , 每次從不可見變成可見+1,而從可見變成不可見-1

onActive和onInactive則分別是處理這兩種狀態的方法,這裏這兩個都是空實現

最後的當變成可見後 會調用 dispatchingValue 通知消息更新

void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

這裏雖然有改while循環,但大部分情況下只會執行一次,因爲mDispatchInvalidated一進來就會設置成false ; 對於併發情況會執行多次,因爲在入口處併發會重新設置成true

有兩種情況,initiator爲空和不爲空,爲空是通知所有的觀察者,不爲空則是通知傳入的initiator;

主要差異是主動和被動

爲null主要場景是setValue等主動設置值的情況,會同步更新所有註冊的監聽

不爲null的主要場景是狀態變更,每個observer自己去刷新數據

然後我們看一下considerNotify方法

 private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
        //
        // we still first check observer.active to keep it as the entrance for events. So even if
        // the observer moved to an active state, if we've not received that event, we better not
        // notify for a more predictable notification order.
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);

這裏會先判斷是否是活躍的,不活躍的直接返回

再判定狀態是否resume的,不是的話就回到上面的狀態變更方法;這裏相當於做了活躍的二次判斷

然後判斷mLastVersionmVersion的大小,只有mVersion比mLastVersion大才會去通知observer去觀察數據,同時把mLastVersion賦值成新的值;這個主要是避免重新分發,如果沒這個判斷,則每次從不可見變成可見都會分發一次最新的消息,這肯定不是我們所想要的結果

然後設置值的方法

    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }

在這裏會把mVersion++ , 並調用上面的dispatchingValue(null)方法通知所有的observer

注意的是

 static final int START_VERSION = -1;

 private int mVersion = START_VERSION;


 private abstract class ObserverWrapper {
        int mLastVersion = START_VERSION;
        ...

}

初始值mVersion和mLastVersion都是 -1 ,而LiveData有兩個構造方法區別一下

  public LiveData(T value) {
        mData = value;
        mVersion = START_VERSION + 1;
    }

    /**
     * Creates a LiveData with no value assigned to it.
     */
    public LiveData() {
        mData = NOT_SET;
        mVersion = START_VERSION;
    }

無參的構造方法mVersion=-1,而傳一個data數據的mVersion=0

就是說,有數據的構造方法,初始值判斷 mVersion> mLastVersion是成立的,而無參的是不成立的

此外,因爲setValue會使mVersion++, 如果在註冊觀察者之前就調用了setValue方法,那麼在註冊後,頁面變成可見的情況下,會強制把之前set的值刷新到方法回調中,正常來說沒有問題;

但在我們設計LiveDataBus總線消息機制的時候,很明顯是不符合消息的流程的,不論是Rxjava還是EventBus都是不允許接收到註冊前的信息的

具體修改得依賴反射,自定義LiveData繼承自MutableLiveData,重寫observer方法

  @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

反射修改LiveData的mVersion值,然後通過反射拿到wrapper對象,反射修改這個對象的mLastVersion值

常規LiveDataBus配置

public class LiveDataBus {
    private static LiveDataBus instance = new LiveDataBus();
    private Map<String, MLiveData> map = new HashMap<>();

    public static LiveDataBus getInstance() {
        return instance;
    }

    public <T> MLiveData<T> getLiveData(String key, Class<T> type) {
        if (!map.containsKey(key)) {
            map.put(key, new MLiveData());
        }
        return (MLiveData<T>) map.get(key);
    }

    public void remove(String key) {
        map.remove(key);
    }
}

public class MLiveData<T> extends MutableLiveData<T> {}

頁面配置

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live_data_bus);
        findViewById(R.id.btn_click).setOnClickListener(this);
        textView = findViewById(R.id.tv_msg);
        liveData = LiveDataBus.getInstance().getLiveData("1234", String.class);
        liveData.setValue("aaa");
        liveData.setValue("bbbb");
        liveData.observe(this, x -> {
                    textView.setText(x + "");
                }
        );
    }

這時候進來,頁面會直接顯示文字 "bbbb" ,但我們註冊是在發送之後,不應該接收到前面的消息

然後我們反射修改成這個樣式就可以了

public class MLiveData<T> extends MutableLiveData<T> {
    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
        super.observe(owner, observer);
        reflect(observer);
    }

    private void reflect(Observer observer) {
        try {
            Field mObserversField = LiveData.class.getDeclaredField("mObservers");
            mObserversField.setAccessible(true);
            Object mObservers = mObserversField.get(this);

            Method getMethod = mObservers.getClass().getDeclaredMethod("get", Object.class);
            getMethod.setAccessible(true);
            Object observerEntry = getMethod.invoke(mObservers, observer);
            //這個值獲取到的是 SafeIterableMap.Entry 實現了Map.Entry接口,需要再次獲取
            //而HashMap中
            // public V get(Object key) {
            //        Node<K,V> e;
            //        return (e = getNode(hash(key), key)) == null ? null : e.value;
            //}
            // 可以看出幫我們自動獲取了value的屬性

            Map.Entry entry = (Map.Entry) observerEntry;
            Object observerWrapper = entry.getValue();
            Field mLastVersionField = observerWrapper.getClass().getSuperclass().getDeclaredField("mLastVersion");
            mLastVersionField.setAccessible(true);
            mLastVersionField.set(observerWrapper, -1);

            Field mVersionField = LiveData.class.getDeclaredField("mVersion");
            mVersionField.setAccessible(true);
            mVersionField.set(this, -1);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意mObserver的get方法獲取到的並不是實際的值

private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
            new SafeIterableMap<>();

這個集合的get方法返回的是一個節點的信息

 protected Entry<K, V> get(K k) {
        Entry<K, V> currentNode = mStart;
        while (currentNode != null) {
            if (currentNode.mKey.equals(k)) {
                break;
            }
            currentNode = currentNode.mNext;
        }
        return currentNode;
    }

  需要再次調用獲取value方法獲取真正的值

而HashMap中

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
}

可以看出幫我們自動獲取了value的屬性,這兩點區分一下

LiveData總結

1.通過observe方法註冊並傳入當前Activity信息,可以感知當期頁面生命週期變化;

當頁面銷燬時候清除所有註冊並直接退出;

當頁面從不可見變成可見時,檢測數據的Version, 根據具體的比較值判斷是否發送最新消息給觀察者對象,判斷標準是當mVersion> mLastVersion 則發送,並把mLastVersion同步成mVersion值,而這兩個對應的初始值都是-1;

mVersion歸屬LiveData,而mLastVersion歸屬ObserverWrapper,對應於上面創建的LifecycleBoundObserver,反射時候需要注意;

引入Version判斷是爲了避免重複發送數據,當活躍狀態頻繁變更而數據未變更導致的多次刷新;

可見性狀態判斷只要是根據聲明的枚舉類型,當前狀態和STARTED狀態進行對比,大於這個狀態即RESUME狀態則認爲是從不可見變成可見,反之則是可見變成不可見,這個屬於被動更新;可見性又可以稱爲活動狀態,意思都差不多

2. 通過setValue或postValue方法通知所有註冊的觀察者數據發生了更新,這個是主動更新,但仍受頁面可見性狀態影響,非可見狀態並不會通知數據更新,需要等下次頁面活動狀態變更纔去刷新;

每次通知都會把當前LiveData的數據版本mVersion++,不論是被動還是主動更新都必須滿足version判定規則;

setValue在主線程中調用,postValue在子線程中調用

3.當註冊前就已經發送了消息更新數據,那麼註冊後第一次活躍狀態變更就會把這個數據刷新給觀察對象;

正常流程無需關心,但涉及消息總線處理則需要額外反射處理LiveData,一般自定義繼承MutableLiveData,在註冊的時候將傳入的對象的observer中的mLastVersion和當前LiveData中的mVersion值修改成初始值-1,這樣註冊之前的數據變更消息便不會接收

 

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