Android Jetpack組件庫LiveData源碼深入探究
Android Jetpack之ViewModel、LiveData
Android Jetpack之DataBinding+ViewModel+LiveData+Room
前言
Jetpack是一個(gè)由多個(gè)技術(shù)庫組成的套件,可幫助開發(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)然這只是我想到的場景,開發(fā)中可以根據(jù)不同的業(yè)務(wù)場景做出不同的判斷。
四、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:: 10Transformations.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組件庫LiveData源碼深入探究的文章就介紹到這了,更多相關(guān)Android Jetpack LiveData內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android?Jetpack?組件LiveData源碼解析
- Android Jetpack 狠活Lifecycles與LiveData使用詳解
- Android Jetpack組件支持庫DataBinding與ViewModel與LiveData及Room詳解
- Android開發(fā)Jetpack組件ViewModel與LiveData使用講解
- Android開發(fā)Jetpack組件LiveData使用講解
- Android?Jetpack庫剖析之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-11
Android編程實(shí)現(xiàn)大圖滾動(dòng)顯示的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)大圖滾動(dòng)顯示的方法,涉及Android使用imageView配合onTouch事件操作圖片顯示的相關(guān)技巧,需要的朋友可以參考下2016-10-10
Android如何判斷當(dāng)前點(diǎn)擊位置是否在圓的內(nèi)部
這篇文章主要為大家詳細(xì)介紹了Android如何判斷當(dāng)前點(diǎn)擊位置是否在圓的內(nèi)部,解析拖動(dòng)圓形控件之內(nèi)響應(yīng)觸摸事件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
Android開發(fā)之在xml中設(shè)置自定義屬性的方法
下面小編就為大家分享一篇Android開發(fā)之在xml中設(shè)置自定義屬性的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01
Android獲取當(dāng)前運(yùn)行的類名或者方法
這篇文章主要介紹了Android獲取當(dāng)前運(yùn)行的類名或者方法,涉及Android操作類與方法的技巧,需要的朋友可以參考下2015-05-05
Android 實(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-07
Android應(yīng)用開發(fā)中使用Fragment的入門學(xué)習(xí)教程
這篇文章主要介紹了Android應(yīng)用開發(fā)中Fragment的入門學(xué)習(xí)教程,可以把Fragment看作為Activity基礎(chǔ)之上的模塊,需要的朋友可以參考下2016-02-02

