Android Jetpack組件庫(kù)LiveData源碼深入探究
Android Jetpack之ViewModel、LiveData
Android Jetpack之DataBinding+ViewModel+LiveData+Room
前言
Jetpack是一個(gè)由多個(gè)技術(shù)庫(kù)組成的套件,可幫助開發(fā)者遵循最佳做法,減少樣板代碼并編寫可在各種Android版本和設(shè)備中一致運(yùn)行的代碼,讓開發(fā)者精力集中編寫重要的代碼。
一、LiveData
LiveData 是一種持有可被觀察的數(shù)據(jù)存儲(chǔ)類。和其他可被觀察的類不同的是LiveData 可以在 Activity ,fragment 或者 service 生命周期發(fā)生改變時(shí)通知更新。LiveData 已經(jīng)是必不可少的一環(huán)了,例如 MVVM 以及 MVI 開發(fā)模式中,都用到了 LiveData。
(注意??源碼是Java代碼)
LIveData 的優(yōu)勢(shì):
- 確保界面符合數(shù)據(jù)狀態(tài):數(shù)據(jù)發(fā)生變化時(shí),就會(huì)通知觀察者。我們可以再觀察者回調(diào)中更新界面,這樣就無需在數(shù)據(jù)改變后手動(dòng)更新界面了。
- 沒有內(nèi)存泄漏,因?yàn)殛P(guān)聯(lián)了生命周期,頁面銷毀后會(huì)進(jìn)行自我清理。
- 不會(huì)因?yàn)锳ctivity 停止而導(dǎo)致崩潰,頁面處于非活躍狀態(tài)時(shí),他不會(huì)接收到任何 LiveData 事件。
- 共享資源,可以使用單例模式擴(kuò)展 LiveData 對(duì)象,以便在應(yīng)用中共享他們。
- 屏幕翻轉(zhuǎn)數(shù)據(jù)狀態(tài)保留
- 不再需要手動(dòng)處理生命周期
- 數(shù)據(jù)始終保持最新狀態(tài)
二、使用案例
LiveData 是一種可用于任何數(shù)據(jù)的封裝容器,通常 LiveData 存儲(chǔ)在 ViewModel 對(duì)象中。
class JokesDetailViewModel : ViewModel() { //創(chuàng)建 LiveData private val _state by lazy { MutableLiveData<JokesUIState>() } val state : LiveData<JokesUIState> = _state private fun loadChildComment(page: Int, commentId: Int, parentPos: Int, curPos: Int) { viewModelScope.launch { launchHttp { jokesApi.jokesCommentListItem(commentId, page)//請(qǐng)求數(shù)據(jù) }.toData { //通知觀察者 _state.value = JokesUIState.LoadMoreChildComment(it.data, parentPos, curPos) } } } } //觀察 LiveData viewModel.state.observe(this, Observer { //更新 UI })
三、LiveData 實(shí)現(xiàn)原理
- Observer:觀察者接口;
- LiveData:發(fā)送已經(jīng)添加觀察的邏輯都在其中;
- ObserverWrapper:抽象的觀察者包裝類;
- LifecycleBoundleObserver:繼承 ObserverWrapper;
- LifecycleEventObserver,生命周期相關(guān)回調(diào);
observe(this,Observer{})
@MainThread public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { //如果已經(jīng)銷毀,直接退出 if (owner.getLifecycle().getCurrentState() == DESTROYED) { return; } //包裝類(下面源碼) LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); //判斷是否已添加 ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); if (existing != null && !existing.isAttachedTo(owner)) { //.... } //之前添加過 if (existing != null) { return; } //注冊(cè) lifecycle,生命周期改變時(shí)會(huì)回調(diào) owner.getLifecycle().addObserver(wrapper); }
LifecycleBoundObserver(owner, observer)
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver { @NonNull final LifecycleOwner mOwner; LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) { super(observer); mOwner = owner; } //當(dāng)前狀態(tài)大于或者等于 STARTED 返回 true @Override boolean shouldBeActive() { return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); } //生命周期相關(guān)回調(diào) @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState(); //如果已經(jīng)銷毀,移除觀察者 if (currentState == DESTROYED) { removeObserver(mObserver); return; } Lifecycle.State prevState = null; //循環(huán)更新狀態(tài) while (prevState != currentState) { prevState = currentState; //修改活躍狀態(tài) activeStateChanged(shouldBeActive()); currentState = mOwner.getLifecycle().getCurrentState(); } } @Override boolean isAttachedTo(LifecycleOwner owner) { return mOwner == owner; } @Override void detachObserver() { mOwner.getLifecycle().removeObserver(this); } }
- 繼承 ObserverWrapper 的包裝類--activeStateChanged(..)方法。
- 實(shí)現(xiàn)LifecycleEventObserver 接口--onStateChanged(..)方法。
ObserverWrapper.activeStateChanged()
void activeStateChanged(boolean newActive) { //如果等于之前狀態(tài) if (newActive == mActive) { return; } mActive = newActive; //活躍 +1,不活躍 -1 (下面有源碼) changeActiveCounter(mActive ? 1 : -1); //如果狀態(tài)變成了活躍狀態(tài),直接調(diào)用 dispatchingValue,傳入當(dāng)前的觀察者 if (mActive) { dispatchingValue(this); //(下面有源碼) } } //修改活躍數(shù)量 void changeActiveCounter(int change) { int previousActiveCount = mActiveCount;//之前活躍的數(shù)量 mActiveCount += change;//總活躍數(shù)量 if (mChangingActiveState) {//如果正在更改,退出 return; } mChangingActiveState = true;//更改中 try { while (previousActiveCount != mActiveCount) { boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0; boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0; previousActiveCount = mActiveCount; //如果當(dāng)前是第一個(gè)激活的,調(diào)用 onActive if (needToCallActive) { onActive();//當(dāng)活動(dòng)的觀察者從0 變成 1的時(shí)候調(diào)用 //如果沒有激活的為 0 ,調(diào)用 onInactive } else if (needToCallInactive) { onInactive();//活躍的觀察者變成 0 時(shí)調(diào)用 } } } finally { mChangingActiveState = false; } } //分發(fā)數(shù)據(jù) void dispatchingValue(@Nullable ObserverWrapper initiator) { if (mDispatchingValue) { mDispatchInvalidated = true; return; //如果正在分發(fā),退出 } mDispatchingValue = true; do { mDispatchInvalidated = false; //如果觀察者不為空 if (initiator != null) { considerNotify(initiator);//對(duì)數(shù)據(jù)進(jìn)行派發(fā),通知觀察者 initiator = null; } else { //如果為空,遍歷所有的觀察者,將數(shù)據(jù)發(fā)送給所有觀察者 for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { considerNotify(iterator.next().getValue()); if (mDispatchInvalidated) { break; } } } } while (mDispatchInvalidated); mDispatchingValue = false; } //數(shù)據(jù)進(jìn)行派發(fā),通知觀察者 private void considerNotify(ObserverWrapper observer) { if (!observer.mActive) { return; } //再次進(jìn)行判斷,如果不活躍,則會(huì)更新狀態(tài),然后退出 if (!observer.shouldBeActive()) { observer.activeStateChanged(false); return; } //觀察者版本是否低于當(dāng)前的版本 //初始值 mLastVersion 為 -1,mVersion 為 0 //小于等于表示沒有需要更新的數(shù)據(jù) if (observer.mLastVersion >= mVersion) { return; } //更新版本 observer.mLastVersion = mVersion; //通知觀察者 observer.mObserver.onChanged((T) mData); }
梳理流程:
1、通過 observe 添加一個(gè)觀察者,這個(gè)觀察者會(huì)被 LifecycleBoundObserver 進(jìn)行一個(gè)封裝,LifecycleBoundObserver繼承 ObserverWrapper,并且實(shí)現(xiàn)了 LifecycleEventObserver。之后就會(huì)將觀察添加到 Observers 中,最后注冊(cè)頁面生命周期的 observer。
2、當(dāng)生命周期發(fā)生變化后,就會(huì)回調(diào)到 LifecycleBoundObserve 中的 onStateChanged 方法中。如果生命周期是銷毀的,就會(huì)移除觀察者,如果不是就會(huì)循環(huán)更新當(dāng)前狀態(tài)。
3、在更新狀態(tài)的時(shí)候就會(huì)判斷是否為活躍狀態(tài),如果是活躍狀態(tài)就會(huì)進(jìn)行分發(fā),分發(fā)的時(shí)候如果觀察者為 null ,就會(huì)遍歷所有的觀察者進(jìn)行分發(fā),否則就分發(fā)傳入的觀察者。
4、最后會(huì)再次判斷活躍狀態(tài),已經(jīng)判斷觀察者版本是否低于當(dāng)前版本,如果都滿足,就會(huì)更新觀察者。
onActive 與 onInactive
如果觀察者的生命周期處于 STARTED 或者 RESUMED 狀態(tài),LiveData 就會(huì)認(rèn)為觀察者處于活躍狀態(tài)。
例如,下載數(shù)據(jù)需要在活躍狀態(tài)下進(jìn)行,或者需要實(shí)時(shí)的監(jiān)聽后臺(tái)數(shù)據(jù),就可以重新下面方法,并完成對(duì)應(yīng)邏輯。
class StockLiveData(url: String) : LiveData<String>() { private val downloadManager = DownloadManager(url) override fun onActive() { if(!downloadManager.isStart()){ downloadManager.start() } } override fun onInactive() { downloadManager.stop() } }
當(dāng)具有活躍的觀察者時(shí),就會(huì)調(diào)用 onActive 方法。
當(dāng)沒有任何活躍的觀察者時(shí),就會(huì)調(diào)用 onInactive 方法。
當(dāng)然這只是我想到的場(chǎng)景,開發(fā)中可以根據(jù)不同的業(yè)務(wù)場(chǎng)景做出不同的判斷。
四、LiveData 相關(guān)源碼
MutableLiveData
由于 LiveData 的發(fā)送數(shù)據(jù)方法是 protected 修飾私有受保護(hù)的,所以不能直接調(diào)用。因此使用MutableLiveData 繼承 LiveData 將發(fā)送數(shù)據(jù)的方法改為了 public。
public class MutableLiveData<T> extends LiveData<T> { /** * Creates a MutableLiveData initialized with the given {@code value}. */ public MutableLiveData(T value) { super(value); } /** * Creates a MutableLiveData with no value assigned to it. */ public MutableLiveData() { super(); } @Override public void postValue(T value) { super.postValue(value); } @Override public void setValue(T value) { super.setValue(value); } }
Transformations.map()
在數(shù)據(jù)分發(fā)給觀察者之前對(duì)其中存儲(chǔ)的值進(jìn)行更改,返回一個(gè)新的 LiveData,可以使用此方法。
val strLiveData = MutableLiveData<String>() val strLengthLiveData = Transformations.map(strLiveData) { it.length } strLiveData.observe(this) { Log.e("---345---> str:", "$it"); } strLengthLiveData.observe(this) { Log.e("---345---> strLength:", "$it"); } strLiveData.value = "hello word" E/---345---> str:: hello word E/---345---> strLength:: 10
Transformations.switchMap()
對(duì)上面的做了個(gè)判斷,根據(jù)不同的需求返回不同的 LiveData。也可以通過 id 去判斷,返回對(duì)應(yīng)的 livedata 即可。
val idLiveData = MutableLiveData<Int>() val userLiveData = Transformations.switchMap(idLiveData) { id-> getUser(id) }
合并多個(gè) LiveData
val live1 = MutableLiveData<String>() val live2 = MutableLiveData<String>() val mediator = MediatorLiveData<String>() mediator.addSource(live1) { mediator.value = it Log.e("---345---> live1", "$it"); } mediator.addSource(live2){ mediator.value = it Log.e("---345---> live2", "$it"); } mediator.observe(this, Observer { Log.e("---345---> mediator", "$it"); }) live1.value = "hello" E/---345---> mediator: hello E/---345---> live1: hello
通過 MediatorLiveData 將兩個(gè) MutableLiveData 合并到一起,這樣當(dāng)任何一個(gè)發(fā)生變化,MediatorLiveData 都可以感知到。
五、LiveData分發(fā)問題
數(shù)據(jù)粘性事件
例如再?zèng)]有觀察者的時(shí)候發(fā)送數(shù)據(jù),此時(shí) mVersion +1,等到真正添加了觀察者后,生命周期也是活躍的,那么就會(huì)將這個(gè)數(shù)據(jù)重新分發(fā)到觀察者。所以說發(fā)送數(shù)據(jù)這個(gè)操作是粘性的。
如果需要去除粘性事件,可以在添加完 observe 后去通過反射修改 mVersion 和 觀察者包裝類中的 mLastVersion 的值,將 mVersion 賦值給 mLastVersion 即可去掉粘性事件。
數(shù)據(jù)倒灌現(xiàn)象
一般情況下,LiveData 都是存放在 ViewModel 中的,當(dāng)Activity重建的時(shí)候,觀察者會(huì)被 remove 掉,重建后會(huì)添加一個(gè)新的觀察者,添加后新的觀察者版本號(hào)就是 -1,所以就會(huì)出現(xiàn)數(shù)據(jù)再次被接收到。
這種解決方式和上面一樣,反射修改版本號(hào)就可以解決。非活躍狀態(tài)的觀察者轉(zhuǎn)為活躍狀態(tài)后,只能接收到最后一次發(fā)送的數(shù)據(jù)。一般情況下我們都需要的是最新數(shù)據(jù),如果非要所有數(shù)據(jù),只能重寫 LiveData 了。
到此這篇關(guān)于Android Jetpack組件庫(kù)LiveData源碼深入探究的文章就介紹到這了,更多相關(guān)Android Jetpack LiveData內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android?Jetpack?組件LiveData源碼解析
- Android Jetpack 狠活Lifecycles與LiveData使用詳解
- Android Jetpack組件支持庫(kù)DataBinding與ViewModel與LiveData及Room詳解
- Android開發(fā)Jetpack組件ViewModel與LiveData使用講解
- Android開發(fā)Jetpack組件LiveData使用講解
- Android?Jetpack庫(kù)剖析之LiveData組件篇
- 詳解Android JetPack之LiveData的工作原理
- Android Jetpack組件中LiveData的優(yōu)劣
相關(guān)文章
Android使用Jetpack WindowManager開發(fā)可折疊設(shè)備(過程分享)
這篇文章主要介紹了Android使用Jetpack WindowManager開發(fā)可折疊設(shè)備,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-11-11Android編程實(shí)現(xiàn)大圖滾動(dòng)顯示的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)大圖滾動(dòng)顯示的方法,涉及Android使用imageView配合onTouch事件操作圖片顯示的相關(guān)技巧,需要的朋友可以參考下2016-10-10Android如何判斷當(dāng)前點(diǎn)擊位置是否在圓的內(nèi)部
這篇文章主要為大家詳細(xì)介紹了Android如何判斷當(dāng)前點(diǎn)擊位置是否在圓的內(nèi)部,解析拖動(dòng)圓形控件之內(nèi)響應(yīng)觸摸事件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05Android開發(fā)之在xml中設(shè)置自定義屬性的方法
下面小編就為大家分享一篇Android開發(fā)之在xml中設(shè)置自定義屬性的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01Android獲取當(dāng)前運(yùn)行的類名或者方法
這篇文章主要介紹了Android獲取當(dāng)前運(yùn)行的類名或者方法,涉及Android操作類與方法的技巧,需要的朋友可以參考下2015-05-05Android 實(shí)現(xiàn)錨點(diǎn)定位思路詳解
本篇文章就使用tablayout、scrollview來實(shí)現(xiàn)android錨點(diǎn)定位的功能。通過<a href="#head" rel="external nofollow" > 去設(shè)置頁面內(nèi)錨點(diǎn)定位跳轉(zhuǎn)。具體實(shí)現(xiàn)思路大家跟隨腳本之家小編一起通過本文看下吧2018-07-07Android應(yīng)用開發(fā)中使用Fragment的入門學(xué)習(xí)教程
這篇文章主要介紹了Android應(yīng)用開發(fā)中Fragment的入門學(xué)習(xí)教程,可以把Fragment看作為Activity基礎(chǔ)之上的模塊,需要的朋友可以參考下2016-02-02