前言
MVVM作為一種架構(gòu)模式,在Android中的主要落地實(shí)踐脫離不開(kāi)兩個(gè)核心類LiveData和ViewModel。閱讀之前需要你具備使用LiveData和ViewModel的基本使用。
有的放矢,帶著目的的看這篇文或許你會(huì)更有收獲。這篇文能幫你解除這些疑惑
- ViewModel怎么實(shí)現(xiàn)多個(gè)Fragment之間數(shù)據(jù)共享?
- Activity橫豎屏切換時(shí),ViewModle是怎么死里逃生的?
- ViewModleScope是怎么感知組件生命周期而自殺(cancel)?
- LiveData如何防止內(nèi)存泄漏風(fēng)險(xiǎn)?
- LiveData的觀察者活躍時(shí)才響應(yīng)是怎么回事?
- 子線程中連續(xù)多次向LiveData發(fā)送值,observer能接受到所有的值嗎?
1)ViewModel
ViewModel的職責(zé),在以注重生命周期的方式存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù)
1.1) ViewModel 特性
1.1.1)注重生命周期
- ViewModel注重組件生命周期主要體現(xiàn)在,當(dāng)橫豎屏轉(zhuǎn)換等配置發(fā)生變化時(shí)導(dǎo)致Activity重建時(shí),ViewModel 不會(huì)被銷毀重建。換句話說(shuō),ViewModel的生命周期要比它所服務(wù)的組件(Activity/Fragment)的生命周期本身要長(zhǎng)。
1.1.2) ViewModel相關(guān)類

a) ViewModelStoreOwner
public interface ViewModelStoreOwner {
ViewModelStore getViewModelStore();
}
- 提供并管理ViewModelStore,在當(dāng)因配置改變發(fā)生重建時(shí),會(huì)保存重建前的ViewModelStore。
- 在Destroy的時(shí)候調(diào)用ViewModelStore的clear()方法
b) ViewModelStore
//有刪減,偽代碼
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
- ViewModelStore里有一個(gè)Map,用以存儲(chǔ)ViewModel;
- 對(duì)外提供獲取、存儲(chǔ)和清空 ViewModel 的方法。
- 當(dāng)clear的時(shí)候會(huì)調(diào)用 ViewModel 的 clear()方法。
c) ViewModel
public abstract class ViewModel {
private final Map<String, Object> mBagOfTags = new HashMap<>();
final void clear() {
..
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
if (obj instanceof Closeable)
((Closeable) obj).close();
}
}
onCleared();
}
<T> T setTagIfAbsent(String key, T newValue) {... }
<T> T getTag(String key) { ...}
}
}
- 關(guān)鍵點(diǎn)內(nèi)部維護(hù)了一個(gè)Map類的mBagOfTags,用以追蹤與該ViewModel對(duì)象相關(guān)的數(shù)據(jù),如viewModelScope,SavedSateHandle數(shù)據(jù)
- 另,被調(diào)用clear()方法時(shí),對(duì)遍歷出mBagOfTags中 Closeable 接口的value,執(zhí)行其close方法。同時(shí)會(huì)調(diào)用自身的onClear()方法
1.2)ViewModel 的構(gòu)建
通常我們使用
//偽代碼
public <T extends ViewModel> T get(Class<T> class){
ViewModel viewModle = viewModelStrore.get(key)
if(viewModel == null){
viewModle = factory.create(class)
viewModelStore.put(key,viewModel);
}
return viewModel
}
flowViewModel = ViewModelProvider(this).get(FlowViewModel::class.*java*)
- ViewModelProviderl兩個(gè)成員屬性,ViewModeStore和ViewModelProvider.Factory。
- 默認(rèn)生成的每個(gè)ViewModle分配一個(gè)key ("
androidx.lifecycle.ViewModelProvider.DefaultKey+modelClass.*canonicalName"*) - 反射調(diào)用ViewModel的構(gòu)造方法,ViewModelStoreOwner中獲取出ViewModelStore起到 [緩存作用],這樣就能夠?qū)崿F(xiàn)在一個(gè)ViewModelStoreOwner范圍內(nèi)能夠?qū)崿F(xiàn)ViewModel的共享。即,多個(gè)組件共同使用一個(gè)ViewModel。
- 最終達(dá)到的效果是在一個(gè)ViewModelStoreOwner范圍內(nèi)只會(huì)創(chuàng)建出一個(gè)ViewModel。
- 哪些 Factory:KeyFactory、
OnRequeryFactory、AndroidViewModelFactory
1.3) ViewModel 與 Kotlin 的 Coroutine 結(jié)合

getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner s,Lifecycle.Event e) {
if (e == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
Fragment/Activity都實(shí)現(xiàn)了ViewModelStoreOwner接口。當(dāng)組件生命周期狀態(tài)切換到ON_DESTORY的的時(shí)候就會(huì)調(diào)用ViewModelStroe的clear()方法。這里有個(gè)isChangeingConfigurations()的判斷。當(dāng)因?yàn)榕渲米兓瘜?dǎo)致的Activity重建時(shí)不會(huì)清除ViewModelStore中的數(shù)據(jù),ViewModelStore會(huì)在新Activity創(chuàng)建時(shí)重新被使用。
當(dāng)調(diào)用其clear()方法的時(shí)候,會(huì)遍歷出內(nèi)部 Closeable 類型的值調(diào)用其close方法。其中viewModelScope作為ViewModel的一個(gè)擴(kuò)展屬性,如下
獲取 ViewModelScope 的時(shí)候,這個(gè) CloseableCoroutineScope 對(duì)象會(huì)被添加到 ViewModle的成員屬性mBagOfTags 這個(gè)Map里。同時(shí)CloseableCoroutineScope實(shí)現(xiàn)了 Closeble 接口。當(dāng)ViewModle clear的時(shí)候被調(diào)用到 CoroutineCotext.cancel() 已達(dá)到取消協(xié)程Job的效果。
2) LiveData

- ViewModel與View層建立起連接主要依靠LiveDataI(當(dāng)然也可Flow)。能夠達(dá)到View層持有ViewModel的引用,ViewModel不持有View的引用。這樣的好處也顯而易見(jiàn),View層和ViewModel層解耦;提高ViewModel的可測(cè)性。
- 實(shí)現(xiàn)方式使用帶自動(dòng)解注冊(cè)的觀察者模式,具體內(nèi)部實(shí)現(xiàn)見(jiàn)下一章節(jié)(2.1),View層拿到ViewModel層的liveData然后注冊(cè)觀察者,當(dāng)LiveData的數(shù)據(jù)發(fā)生變化時(shí),自動(dòng)更新UI。
- 因?yàn)榫邆渥詣?dòng)解注冊(cè)功能,所以天然的能夠防止內(nèi)存泄漏(destory時(shí)清除掉原理見(jiàn)下文)
2.1 ) 自動(dòng)解注冊(cè)是怎么實(shí)現(xiàn)的?
在看自動(dòng)解注冊(cè)之前先看怎么注冊(cè)的觀察者的
//偽代碼有刪減
@MainThread
public void observe( LifecycleOwner owner, Observer<? super T> observer) {
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
owner.getLifecycle().addObserver(wrapper);
}
LiveData的Oberver()方法中主要做兩件事,
- 把observer存在自己的一個(gè)Map類型的mObservers屬性里。
- observer被包裝成一個(gè)LifecycleBoundObserver,這個(gè)LifecycleBoundObserver繼承了LifecycleEventObserver,因此這個(gè)observer也會(huì)被注冊(cè)到livefeCycle上。
這樣這個(gè)LifecycleBoundObserver對(duì)象就可以同時(shí)觀察監(jiān)聽(tīng)LiveData的事件和LifeCycle的事件。這樣在對(duì)應(yīng)的LifeCycle的事件里就可以做對(duì)應(yīng)的處理。比如移除從注冊(cè),不活躍時(shí)不通知回調(diào)。
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
...
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (currentState == DESTROYED) {
ObserverWrapper removed = mObservers.remove(observer); //從與LiveData解綁
removed.detachObserver() //與生命周期組件接綁
}
...
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
2.2)觀察者活躍時(shí)才響應(yīng)是怎么回事?
- LiveData的監(jiān)聽(tīng)者observer在內(nèi)部被包裝成了LifecycleBoundObserver如上一章節(jié)描述,這個(gè)observer所監(jiān)聽(tīng)的生命周期組件如果處于ON_START 或者 ON_RESUME時(shí)該監(jiān)聽(tīng)者才會(huì)被通知回調(diào)。
內(nèi)部實(shí)現(xiàn)也比較簡(jiǎn)單,主要是依托observer監(jiān)聽(tīng)了組件生命周期,自身進(jìn)入決定自身是否是處于active狀態(tài)。
- 有一個(gè)問(wèn)題,如果Observer從非活躍狀態(tài)變?yōu)榛钴S狀態(tài)一定會(huì)被回調(diào)嗎?答案是不一定。
原因,liveData內(nèi)部維護(hù)了一個(gè)版本,當(dāng)恢復(fù)到活躍狀態(tài)時(shí),如果oberver中版本小于了liveData的版本,才會(huì)把最近的值分發(fā)非observer進(jìn)而觸發(fā)回調(diào)方法。liveData中版本會(huì)雖然每次新值的設(shè)置自動(dòng)+1,代碼如下。也就是說(shuō),當(dāng)observer處于非活躍狀態(tài)期間,如果liveData沒(méi)有新值被設(shè)定進(jìn)來(lái),當(dāng)observer回到活躍狀態(tài)的時(shí)候不會(huì)被通知回調(diào)。
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
- 又有另外一個(gè)問(wèn)題,既然不會(huì)被重新回調(diào),網(wǎng)上說(shuō)的數(shù)據(jù)倒灌是怎么回事?
2.3) 子線程中連續(xù)多次向LiveData發(fā)送值,observer能接受到所有的值嗎?
fun postValueIntWorkThread(){
liveData.post(1)
liveData.post(2)
}
LiveData有一個(gè)理念,只保留最新值。體現(xiàn)在兩點(diǎn)
- 短時(shí)間內(nèi)多次從子線程向liveData post值的時(shí)候,observer不保證每個(gè)值都收到通知。具體實(shí)現(xiàn)如下代碼:
- 當(dāng)observer處于非活躍狀態(tài)期間,liveData中多次設(shè)置了值,當(dāng)liveData恢復(fù)活躍狀態(tài)后,只會(huì)接受到最新值。
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;//注釋3
mPendingData = NOT_SET;//注釋4
}
setValue((T) newValue);
}
};
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;//注釋5
mPendingData = value;//注釋2處,value賦值給mPendingData
}
if (!postTask) {//注釋1:有值正在分發(fā)過(guò)程中
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
這段代碼比較有意思
1> 注釋5處,進(jìn)入被mDataLock這個(gè)monitor劃定的臨界區(qū);計(jì)算出postTask的值,如果postTask為ture說(shuō)明當(dāng)前沒(méi)有等待被分發(fā)的值,如果值為false說(shuō)明有正在被分發(fā)的值。
2> 注釋2處,無(wú)論postTask的值為true或者false,都會(huì)把value的值賦值給mPendingData。
3 >注釋3處,如果有正在分發(fā)的值,就不執(zhí)行接下來(lái)的操作了。此時(shí)注釋2處已經(jīng)把值賦值給了mPendingData,這個(gè)mPending還能分發(fā)嗎?
4>注釋3處這段邏輯通過(guò)handler機(jī)制,最終在Main線程中執(zhí)行,把mPendingData賦值給newValue,最終newValue被分發(fā)出去了
上面這個(gè)步驟下來(lái),思考這樣一種情況,連續(xù)post 1和2兩個(gè)值,當(dāng)post 2的時(shí)候,如果此時(shí)mPendingData值還為1,那么postTask就為false。但還是把值賦值給了mPendingData。接下來(lái),注釋1處正好符合判斷條件,就不會(huì)向下執(zhí)行。 當(dāng)主線程執(zhí)行到注釋3處的runable時(shí),會(huì)把2復(fù)制給newValue,最后把值分發(fā)出去了。
小結(jié)
本文主要圍繞ViewModel和LiveData一些特性以及內(nèi)部實(shí)現(xiàn)展開(kāi),閱讀完之后,開(kāi)頭的那幾個(gè)問(wèn)題能找到答案了嗎?
最后,我整理了一些學(xué)習(xí)資料,里面包括Java基礎(chǔ)、Android進(jìn)階、架構(gòu)設(shè)計(jì)、NDK、音視頻開(kāi)發(fā)、跨平臺(tái)、底層源碼等技術(shù),還有2022年一線大廠最新面試題集錦,都分享給大家,助大家學(xué)習(xí)路上披荊斬棘~ 能力得到提升,思維得到開(kāi)闊~ 有需要的可以點(diǎn)擊下方公號(hào)鏈接免費(fèi)獲取。
公眾號(hào)鏈接:https://mp.weixin.qq.com/s/ZDvWgZ_huo4natNk26ovlw
