LiveData源碼解析

是什么

是一個可觀察的數(shù)據(jù)存儲類疼鸟,且具備宿主生命周期的感知能力。

優(yōu)勢

  • 頁面不可見時不會派發(fā)消息
  • 頁面可見時,會立刻派發(fā)最新的一條消息給所有觀察者--保證頁面最新狀態(tài)
  • 不再需要手動處理生命周期匿级,避免NPE
  • 支持黏性事件的分發(fā)
  • 共享資源,可以使用單例模式拓展 LiveData科雳,實現(xiàn)全局的根蟹,不用反注冊脓杉,不會內(nèi)存泄漏的消息分發(fā)總線糟秘。

各類之間的關系

image.png

注冊觀察者流程,事件分發(fā)原理

// LiveData.class
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        // 判斷是否主線程
        assertMainThread("observe");
        
        // 如果宿主已經(jīng)destroy球散,則return
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            return;
        }
        
        // 用LifecycleBoundObserver包裝observer尿赚,其中包含LifecycleOwner,且能響應生命周期事, 如果頁面destroy了,可以將observer自動從lifecycle移除
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        
        // 將observer和wrapper保存起來凌净,如果之前已經(jīng)存在則返回之前的wrapper悲龟,否則返回null
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        
        // 如果之前已經(jīng)添加過該observer,且observer不是綁定當前owner冰寻,拋出異常
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        // 如果之前已經(jīng)添加過該observer须教,則不再添加,return
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }
    
// 更新mActiveCount斩芭,判斷是否觸發(fā)LiveData的onActive()和onInactive()
@MainThread
void changeActiveCounter(int change) {
        int previousActiveCount = mActiveCount;
        mActiveCount += change;
        if (mChangingActiveState) {
            return;
        }
        mChangingActiveState = true;
        try {
            // 更新mActiveCount
            while (previousActiveCount != mActiveCount) {
                boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;
                boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;
                previousActiveCount = mActiveCount;
                
                // 觸發(fā)onActive()或onInactive()
                // 可以充分利用 onActive() 方法被激活的時機轻腺,來實現(xiàn)一些數(shù)據(jù)懶加載的功能。
                if (needToCallActive) {
                    onActive();
                } else if (needToCallInactive) {
                    onInactive();
                }
            }
        } finally {
            mChangingActiveState = false;
        }
}

// 給Observer分發(fā)數(shù)據(jù)
void dispatchingValue(@Nullable ObserverWrapper initiator) {
        // 如果正在分發(fā)數(shù)據(jù)划乖,則將mDispatchInvalidated置為true贬养,后面分發(fā)結束后會再分發(fā)一次
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            // 如果initiator則只給initiator分發(fā),否則給所有observer分發(fā)數(shù)據(jù)
            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());
                    // 如果正在分發(fā)數(shù)據(jù)琴庵,dispatchingValue(observer)又被調用了误算,則停止本次分發(fā),重新開始分發(fā)
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
}

// 真正分發(fā)數(shù)據(jù)的地方
private void considerNotify(ObserverWrapper observer) {
        // 如果observer所在頁面不是活躍狀態(tài)迷殿,則不分發(fā)數(shù)據(jù)
        if (!observer.mActive) {
            return;
        }
        
        // 獲取observer所在頁面的最新狀態(tài)儿礼,有可能頁面狀態(tài)變了,但是還沒更新Observer的mActive
        // 我們?nèi)匀幌扰袛鄌bserver.mActive庆寺,如果頁面已經(jīng)變?yōu)榛钴S狀態(tài)蜘犁,但是observer.mActive還沒更新,即還是不活躍狀態(tài)止邮,則更新observer.mActive这橙,但仍不分發(fā)數(shù)據(jù)
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        
        // observer的數(shù)據(jù)版本大于LiveData的版本,即接收數(shù)據(jù)的次數(shù)大于發(fā)送數(shù)據(jù)的次數(shù)导披,說明observer已經(jīng)收到最新的數(shù)據(jù)屈扎,不再回調observer#onChanged()通知更新
        // observer的mLastVersion=-1,如果之前已經(jīng)發(fā)送過數(shù)據(jù)了撩匕,即mLastVersion<mVersion鹰晨,則會出現(xiàn)黏性事件,后注冊的觀察者收到了前面發(fā)送的消息
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        
        // 每分發(fā)一次消息止毕,則把觀察者和LiveData的version對齊模蜡,防止重復發(fā)送
        observer.mLastVersion = mVersion;

        // 通知更新
        observer.mObserver.onChanged((T) mData);
}
    
    
#########################################################


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

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

        // 當前界面是否活躍,即當前界面狀態(tài)是否大于STARED
        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        // 當生命周期變化時回調
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
                
            // 獲取當前界面狀態(tài)
            Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
            
            // 如果當前界面已經(jīng)銷毀扁凛,則移除observer
            if (currentState == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            
            Lifecycle.State prevState = null;
            while (prevState != currentState) {
                prevState = currentState;
                // 給父類OvserverWrapper設置活躍狀態(tài)忍疾,如果是活躍的話分發(fā)數(shù)據(jù)
            activeStateChanged(shouldBeActive());
                currentState = mOwner.getLifecycle().getCurrentState();
            }
        }

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

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

// ObserverWrapper.class
    private abstract class ObserverWrapper {
        final Observer<? super T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION;

        ObserverWrapper(Observer<? super T> observer) {
            mObserver = observer;
        }

        abstract boolean shouldBeActive();

        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }

        void detachObserver() {
        }

        // 設置活躍狀態(tài),如果是活躍的話分發(fā)數(shù)據(jù)
        void activeStateChanged(boolean newActive) {
            // 狀態(tài)一樣不做處理
            if (newActive == mActive) {
                return;
            }
            
            mActive = newActive;
            // 更新mActiveCount谨朝,判斷是否出發(fā)LiveData的onActive()和onInactive()
            changeActiveCounter(mActive ? 1 : -1);
            
            // 如果是活躍的話卤妒,分發(fā)數(shù)據(jù)
            if (mActive) {
                dispatchingValue(this);
            }
        }
    }
image.png

小結

  1. 首先調用LiveData#observe(owner, observer)進行注冊甥绿,這時會將observer包裝成LifecycleBoundObserver
  2. LifecycleBoundObserver繼承自ObserverWrapper乍狐,保存了Observer和LifecycleOwner脚囊,所以可以回調observer#onChanged(data),也可以從LifecycleOwner中獲得頁面活躍狀態(tài)谭确;同時它實現(xiàn)了LifecycleEventObserver士复,所以能夠監(jiān)聽lifecycle的生命周期事件图谷,在頁面銷毀時移除observer,在頁面變?yōu)榛钴S狀態(tài)時阱洪,接收最新的數(shù)據(jù)(如果之前沒有接收過的話)蜓萄。
  3. 如果是在頁面onResume是注冊的觀察者,onStateChanged(owner, event)會回調3次:onCreate澄峰、onStart嫉沽、onResume。但是observer#onChanged(data)只會回調一次俏竞,因為onStateChanged(owner, event)會判斷如果頁面狀態(tài)和之前一樣(mActive沒有改變)绸硕,則return。
  4. 另外即使mActive改變了魂毁,但是如果數(shù)據(jù)沒有改變玻佩,也不會回調observer#onChanged(data),因為LiveData會根據(jù)ObserverWrapper#mLastVersion和自身的mVersion判斷數(shù)據(jù)的接收次數(shù)和發(fā)送次數(shù)是否一致:mLastVersion<mVersion則發(fā)送席楚;否則不發(fā)送咬崔。
  5. LifecycleBoundObserver#onStateChanged(owner, event)回調時,會根據(jù)是否有活躍的觀察者(mActiveCount)來觸發(fā)LiveData#onActive()/onInactive()
  6. 之后就會判斷頁面是否活躍烦秩,如果活躍則分發(fā)數(shù)據(jù)垮斯,調用considerNotify方法,通過ObserverWrapper#mLastVersion和LiveData#mVersion來判斷是否觸發(fā)Observer#onChanged(data)

發(fā)送數(shù)據(jù)

// LiveData.class

private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            // 加鎖只祠,防止主線程更新數(shù)據(jù)時兜蠕,mPendingData被改變
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
            setValue((T) newValue);
        }
};
    
    
protected void postValue(T value) {
        boolean postTask;
        // 加鎖,防止主線程更新數(shù)據(jù)時抛寝,mPendingData被改變
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        // 如果postTask=false熊杨,說明正在等待runnable獲取mPendingData更新界面,不再發(fā)送runnable
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

@MainThread
protected void setValue(T value) {
        assertMainThread("setValue");
        // 增加發(fā)送次數(shù)
        mVersion++;
        mData = value;
        // 通知所有的observer更新數(shù)據(jù)
        dispatchingValue(null);
}
image.png

數(shù)據(jù)倒灌是什么

頁面在再次打開時盗舰,收到了舊數(shù)據(jù)晶府。
比如,有3個fragment:列表钻趋、詳情川陆、編輯,它們都共享viewmodel中的同一個liveData爷绘,在編輯頁編輯完后通過livedata保存數(shù)據(jù)书劝,退出返回詳情頁時,詳情頁恢復活躍狀態(tài)土至,會從liveData收到最新的數(shù)據(jù)更新界面购对。再次回退到列表頁,點擊另一個item打開詳情頁陶因,這時詳情頁調用liveData#addObserver()會從liveData收到之前到舊數(shù)據(jù)骡苞,這就是數(shù)據(jù)倒灌。

如果解決黏性事件

  1. 反射干涉Version
    不推薦這種方式楷扬,網(wǎng)上的方案是在LiveData#observe()方法中利用反射修改LifecycleBoundObserver中的mLastVersion的值=LiveData的mVersion解幽,但是這種方案只適合在頁面的onCreate(頁面還不是活躍狀態(tài))時注冊observer,因為這時頁面的state是INITIALIZED烘苹,LiveData#observe()->LifecycleRegistrty#addObserver()中由于LifecycleRegistrty.mState=LifecycleBoundObserver.mState躲株,導致不會回調ObserverWithState#dispatchEvent()->LifecycleBoundObserver#onStateChanged(),從而不會觸發(fā)observer.onChanged()镣衡,也就達到了取消粘性事件的目的霜定。但是如果在onStart()或之后(頁面變?yōu)榛钴S狀態(tài))注冊observer,由于這時頁面的state是大于INITIALIZED的廊鸥, 所以在LiveData#observe()->LifecycleRegistrty#addObserver()中會觸發(fā)ObserverWithState#dispatchEvent()->LifecycleBoundObserver#onStateChanged()望浩,也就導致observer.onChanged()被觸發(fā),還是發(fā)生粘性事件惰说。

  2. 自定義LiveData和Observer磨德,分別在內(nèi)部維護一個version。當不需要粘性事件時吆视,在用LiveData注冊Observer時典挑,將LiveData的version賦值給observer的version;當需要粘性事件時啦吧,在用LiveData注冊Observer時搔弄,設置Observer的version為-1。

參考:https://github.com/KunMinX/UnPeek-LiveData/blob/de958b679b/unpeeklivedata/src/main/java/com/kunminx/architecture/ui/callback/ProtectedUnPeekLiveData.java#L40-L176

public class ProtectedUnPeekLiveData<T> extends LiveData<T> {


  private final static int START_VERSION = -1;


  private final AtomicInteger mCurrentVersion = new AtomicInteger(START_VERSION);


  protected boolean isAllowNullValue;


  /**
   * TODO tip:當 liveData 用作 event 用途時丰滑,可使用該方法來觀察 "生命周期敏感" 的非粘性消息
   * <p>
   * state 是可變且私用的顾犹,event 是只讀且公用的,
   * state 的倒灌是應景的褒墨,event 倒灌是不符預期的炫刷,
   * <p>
   * 如果這樣說還不理解,詳見《LiveData 唯一可信源 讀寫分離設計》的解析:
   * https://xiaozhuanlan.com/topic/2049857631
   *
   * @param owner    activity 傳入 this郁妈,fragment 建議傳入 getViewLifecycleOwner
   * @param observer observer
   */
  @Override
  public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    super.observe(owner, createObserverWrapper(observer, mCurrentVersion.get()));
  }


  /**
   * TODO tip:當 liveData 用作 event 用途時浑玛,可使用該方法來觀察 "生命周期不敏感" 的非粘性消息
   *
   * @param observer observer
   */
  @Override
  public void observeForever(@NonNull Observer<? super T> observer) {
    super.observeForever(createObserverWrapper(observer, mCurrentVersion.get()));
  }


  /**
   * TODO tip:當 liveData 用作 state 用途時,可使用該方法來觀察 "生命周期敏感" 的粘性消息
   *
   * @param owner    activity 傳入 this噩咪,fragment 建議傳入 getViewLifecycleOwner
   * @param observer observer
   */
  public void observeSticky(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
    super.observe(owner, createObserverWrapper(observer, START_VERSION));
  }


  /**
   * TODO tip:當 liveData 用作 state 用途時顾彰,可使用該方法來觀察 "生命周期不敏感" 的粘性消息
   *
   * @param observer observer
   */
  public void observeStickyForever(@NonNull Observer<? super T> observer) {
    super.observeForever(createObserverWrapper(observer, START_VERSION));
  }


  /**
   * TODO tip:只需重寫 setValue
   * postValue 最終還是會經(jīng)過這里
   *
   * @param value value
   */
  @Override
  protected void setValue(T value) {
    mCurrentVersion.getAndIncrement();
    super.setValue(value);
  }


  /**
   * TODO tip:
   * 1.添加一個包裝類极阅,自己維護一個版本號判斷,用于無需 map 的幫助也能逐一判斷消費情況
   * 2.重寫 equals 方法和 hashCode涨享,在用于手動 removeObserver 時筋搏,忽略版本號的變化引起的變化
   */
  class ObserverWrapper implements Observer<T> {
    private final Observer<? super T> mObserver;
    private int mVersion = START_VERSION;


    public ObserverWrapper(@NonNull Observer<? super T> observer, int version) {
      this.mObserver = observer;
      this.mVersion = version;
    }


    @Override
    public void onChanged(T t) {
      if (mCurrentVersion.get() > mVersion && (t != null || isAllowNullValue)) {
        mObserver.onChanged(t);
      }
    }


    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }
      if (o == null || getClass() != o.getClass()) {
        return false;
      }
      ObserverWrapper that = (ObserverWrapper) o;
      return Objects.equals(mObserver, that.mObserver);
    }


    @Override
    public int hashCode() {
      return Objects.hash(mObserver);
    }
  }


  /**
   * TODO tip:
   * 通過 ObserveForever 的 Observe,需要記得 remove厕隧,不然存在 LiveData 內(nèi)存泄漏的隱患奔脐,
   * 保險的做法是,在頁面的 onDestroy 環(huán)節(jié)安排 removeObserver 代碼吁讨,
   * 具體可參見 app module 中 ObserveForeverFragment 的案例
   *
   * @param observer observeForever 注冊的 observer髓迎,或 observe 注冊的 observerWrapper
   */
  @Override
  public void removeObserver(@NonNull Observer<? super T> observer) {
    if (observer.getClass().isAssignableFrom(ObserverWrapper.class)) {
      super.removeObserver(observer);
    } else {
      super.removeObserver(createObserverWrapper(observer, START_VERSION));
    }
  }


  private ObserverWrapper createObserverWrapper(@NonNull Observer<? super T> observer, int version) {
    return new ObserverWrapper(observer, version);
  }


  /**
   * TODO tip:
   * 手動將消息從內(nèi)存中清空,
   * 以免無用消息隨著 SharedViewModel 的長時間駐留而導致內(nèi)存溢出的發(fā)生建丧。
   */
  public void clear() {
    super.setValue(null);
  }


}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末排龄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子翎朱,更是在濱河造成了極大的恐慌涣雕,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闭翩,死亡現(xiàn)場離奇詭異挣郭,居然都是意外死亡,警方通過查閱死者的電腦和手機疗韵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門兑障,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蕉汪,你說我怎么就攤上這事流译。” “怎么了者疤?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵福澡,是天一觀的道長。 經(jīng)常有香客問我驹马,道長革砸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任糯累,我火速辦了婚禮算利,結果婚禮上,老公的妹妹穿的比我還像新娘泳姐。我一直安慰自己效拭,他們只是感情好,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缎患,像睡著了一般慕的。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挤渔,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天肮街,我揣著相機與錄音,去河邊找鬼蚂蕴。 笑死低散,一個胖子當著我的面吹牛俯邓,可吹牛的內(nèi)容都是我干的骡楼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼稽鞭,長吁一口氣:“原來是場噩夢啊……” “哼鸟整!你這毒婦竟也來了?” 一聲冷哼從身側響起朦蕴,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤篮条,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吩抓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涉茧,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年疹娶,在試婚紗的時候發(fā)現(xiàn)自己被綠了伴栓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡雨饺,死狀恐怖钳垮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情额港,我是刑警寧澤饺窿,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站移斩,受9級特大地震影響肚医,放射性物質發(fā)生泄漏。R本人自食惡果不足惜向瓷,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一忍宋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧风罩,春花似錦糠排、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哺徊。三九已至,卻和暖如春乾闰,著一層夾襖步出監(jiān)牢的瞬間落追,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工涯肩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留轿钠,地道東北人。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓病苗,卻偏偏與公主長得像疗垛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子硫朦,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容