關(guān)于MVVM的一些秘密

前言

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容