#頭條創作挑戰賽#
本文通過在鬥魚直播遇到的問題總結而出,如有不對的地方,請及時批評指正。篇幅較長,請耐心閱讀。
大家在日常開發工作中使用LiveData是否遇到過這樣一個場景,使用LiveData發送資料時,當手機螢幕發生旋轉時,資料會重新自動發送一遍。這個就是經典的LiveData資料倒灌問題,也被稱為資料粘性問題。
本文從以下幾個子產品進行分析,由簡到繁,深入源碼分析,幫助深入了解LiveData實作原理,追本溯源,從根本上解決問題。
- 簡介
- 基本使用
- 源碼分析之生命周期綁定
- 源碼分析之資料發送
- 資料倒灌問題解決方案
簡介
LiveData最為Jetpack元件重要元件之一,是一種可觀察的資料存儲類,支援存儲多種資料類型。
基本使用
建立一個LiveData執行個體,添加資料。
class VideoDetailModel : ViewModel() {
val shareCodeLiveData: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
fun getShareCode(dto: ShareCode) {
//切換IO
viewModelScope.launch(Dispatchers.IO) {
asyncTask {
//網絡請求
getService(TieApi::class.java).getShareCode(dto)
}.result.data.also {
//改變資料源,通知觀察者
shareCodeLiveData.post(it)
}
}
}
}
在Activity中綁定LiveData并監聽資料變化。
//在activity中觀察資料變化
shareCodeLiveData.observe(this)Observer {
//根據code 進行UI展示
}
源碼分析
生命周期綁定
LiveData使用observe 綁定Activity生命周期,我們從源碼層面來看一下observe是怎麼綁定Activity生命周期的。
1.Activity作為LifecycleOwner接口的實作者,可以通過LifecycleOwner的getLifecycle()方法擷取LifeCycle。并綁定目前Activity生命周期。當生命周期發生變化時,會通知LiveData。
@MainThread //主線程
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
//通過lifecycle判斷目前activity狀态,如果是destory 則不會監聽資料變化
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//關聯一下observer和LifecycleOwner
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//儲存觀察者,如果已經添加過,則傳回對應的value,否則傳回null
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
..............省略部分代碼.......
//注冊LifeCycle監聽,綁定生命周期
owner.getLifecycle().addObserver(wrapper);
}
2.LifecycleBoundObserver不僅儲存了 LifecycleOwner 和 observer。還繼承了LifecycleEventObserver,當LifecycleOwner生命周期發生變化時,會調用LifecycleEventObserver.onStateChanged方法。
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//通過lifecycleOwner擷取目前activity生命周期狀态
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
//如果activity被銷毀則移除監聽
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
//記錄Activity上一個生命周期狀态
Lifecycle.State prevState = null;
//更新活躍狀态
while (prevState != currentState) {
prevState = currentState;
//改變活躍狀态STARTED
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
//目前生命周期為STARTED時,為活躍狀态
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
3.LifecycleBoundObserver是LifeCycle觀察者的一個子類,通過觀察LifecycleOwner生命周期狀态,改變LiveData的監聽。
void activeStateChanged(boolean newActive) {
//如果目前狀态和上一個狀态一樣,則已經注冊過了,不需要處理
if (newActive == mActive) {
return;
}
//更新目前活躍狀态
mActive = newActive;
//修改活躍狀态數量
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
//分發資料
dispatchingValue(this);
}
}
LiveData通過擷取Activity的LifeCycle來判斷目前Activity顯示狀态,判斷條件為Activity生命周期為onStart時,進行注冊監聽,資料分發。
資料分發
1 .當Activity目前生命周期為onStart時,調用 dispatchingValue(this)進行資料分發。下面我們來看一下具體資料是怎麼進行分發的?
void dispatchingValue(@Nullable ObserverWrapper initiator) {
//是否正在進行資料分發
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
//如果觀察者不為null
if (initiator != null) {
//分發資料給目前觀察者
considerNotify(initiator);
initiator = null;
} else {
//目前觀察者為null,則分發資料給其他所有觀察者
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
2.不管注冊多少觀察者,最後都會調用considerNotify進行資料的具體分發操作。我們接着往下看。
private void considerNotify(ObserverWrapper observer) {
//如果目前觀察者不處于活躍狀态 ,則傳回退出
if (!observer.mActive) {
return;
}
//如果目前觀察者不處于活躍狀态 ,則更新狀态後 傳回退出
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//如果觀察者的版本小于Livedata中記錄的版本,則傳回
if (observer.mLastVersion >= mVersion) {
return;
}
//将目前觀察者版本更新給mLastVersion
observer.mLastVersion = mVersion;
//通知觀察者資料變化
observer.mObserver.onChanged((T) mData);
}
資料分發時,首先判斷目前觀察者Activity是否處理活躍狀态(onStart),并且目前觀察者的版本是否小于Livedata中記錄的版本。隻有目前觀察者的版本小于LiveData中記錄的版本才會進行資料分發。
發送同步資料
@MainThread //強制主線程
protected void setValue(T value) {
assertMainThread("setValue");
//将目前版本加1
mVersion++;
mData = value;
//通知是以觀察者更新資料
dispatchingValue(null);
}
LiveData.setValue隻能在主線程中調用,同時使用mVersion記錄目前LiveData資料版本,然後直接調用dispatchingValue(null) 進行資料分發。
發送異步資料
protected void postValue(T value) {
boolean postTask;
//加鎖
synchronized (mDataLock) {
//目前沒有資料處理,将postTask 設定位true
postTask = mPendingData == NOT_SET;
//将資料交個mPendingData
mPendingData = value;
}
//如果前面還有資料需要處理,則傳回
if (!postTask) {
return;
}
//将資料處理postToMainThread交給線程池處理
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
LiveData.postValue将資料發送到線程池中處理并切換至主線程,使用postTask用來标記前面是否還有資料處理。我們來看一下mPostValueRunnable中的資料處理。
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
//加鎖
synchronized (mDataLock) {
//将需要處理的資料交給newValue
newValue = mPendingData;
//重置mPendingdata
mPendingData = NOT_SET;
}
//分發資料
setValue((T) newValue);
}
};
run()方法中主要是調用setValue進行資料分發,當資料處理完将mPendingData恢複到初始值。
資料倒灌問題解決方案
出現此問題的原因在于:如果目前螢幕發生旋轉,Activity會發生銷毀重建時導緻觀察者Activity被移除監聽。當Activity重建的時候會重新添加一個新的觀察者,由于ViewModel可以儲存LiveData資料不被回收。當Activity生命周期變成onStart時,新的觀察者的版本号為初始值-1,小于LiveData中記錄的版本号mVersion,會導緻資料再次被分發一遍。
關于ViewModel儲存資料可參考這篇文章:《網易一面之ViewModel存儲原理面試總結》。
private void considerNotify(ObserverWrapper observer) {
//如果目前觀察者不處于活躍狀态 ,則傳回退出
if (!observer.mActive) {
return;
}
//如果目前觀察者不處于活躍狀态 ,則更新狀态後 傳回退出
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//如果觀察者的版本小于Livedata中記錄的版本,則傳回
if (observer.mLastVersion >= mVersion) {
return;
}
//将目前觀察者版本更新給mLastVersion
observer.mLastVersion = mVersion;
//通知觀察者資料變化
observer.mObserver.onChanged((T) mData);
}
解決方案
當Activity恢複重建時,通過反射技術修改觀察者中的mLastVersion版本号,使mLastVersion等于LiveData中記錄的版本号mVersion,此時observer.mLastVersion >= mVersion這個判斷條件成立,不會進行資料分發。
以上就是鬥魚直播面試後總結的幾個要點,還不會的同學趕緊學起來吧,感謝您的閱讀,創造不易,如果您覺得本篇文章對您有幫助,請點選關注小編,您的支援就是小編創作的最大動力