05 SavedState組件架構(gòu)原理解析

前言

在擁抱了Jetpack之后,我們通常使用ViewModel組件來(lái)管理數(shù)據(jù),但ViewModel只能當(dāng)頁(yè)面因配置變更而重建時(shí)才能復(fù)用,但如果是內(nèi)存不足或者電量不足等系統(tǒng)原因導(dǎo)致的頁(yè)面被回收時(shí)ViewModel是不會(huì)被復(fù)用的。

我們都知道Activity有著一套 onSaveInstanceState-onRestoreInstanceState 狀態(tài)保存機(jī)制,旨在頁(yè)面因系統(tǒng)原因被回收時(shí)可以保存轉(zhuǎn)改,頁(yè)面重建后,可以恢復(fù)之前的狀態(tài),為用戶提供更好的體驗(yàn)。

但是ViewModel是無(wú)法直接感知onSaveInstanceState被處罰的時(shí)機(jī)的。

于是乎,SavedState這個(gè)中間組件就誕生了,它能夠幫助開(kāi)發(fā)者在ViewModel中處理Activity和fragment狀態(tài)保存和恢復(fù)。

什么是SavedState?

在頁(yè)面即將被銷毀的時(shí)候,每個(gè)使用SavedState的ViewModel都會(huì)創(chuàng)建一個(gè)Bundle來(lái)存儲(chǔ)自己的這份數(shù)據(jù),最后這些Bundle會(huì)被匯總到一個(gè)Bundle中,然后再保存到onSaveInstanceState(Bundle outState)的outState中。

當(dāng)頁(yè)面回復(fù)的時(shí)候,會(huì)從onCreate(Bundle savedInstanceState)中的savedInstanceState中取出原來(lái)存放的總的那個(gè)Bundle,然后再取出一個(gè)個(gè)的術(shù)語(yǔ)ViewModel的子Bundle,于是我們就能愉快的在ViewModel中復(fù)用之前存儲(chǔ)的數(shù)據(jù)了。

其實(shí)就是利用Bundle可以保存另一個(gè)Bundle這么一個(gè)特點(diǎn),分層分區(qū)保存數(shù)據(jù),讓數(shù)據(jù)之間相互分離,進(jìn)而方便整存整取。

SavedState的用法

首先還是添加依賴:

api 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'
ViewModel搭配SavedState實(shí)現(xiàn)數(shù)據(jù)復(fù)用

當(dāng)Activity的onSaveInstanceState方法被執(zhí)行的時(shí)候,這個(gè)實(shí)際會(huì)觸發(fā)SavedStateHandle的Bundle saveState()方法,所以它的數(shù)據(jù)才能被存儲(chǔ)。

當(dāng)Activity被恢復(fù)的時(shí)候,在新建ViewModel實(shí)例對(duì)象的時(shí)候,會(huì)從Activity的savedInstanceState中提取之前存儲(chǔ)數(shù)據(jù),然后構(gòu)造SavedStateHandle對(duì)象并把恢復(fù)的數(shù)據(jù)賦值給它,隨后把SavedStateHandle傳遞到ViewModel中。所以它的數(shù)據(jù)才能被復(fù)用。

//我們只需要在構(gòu)造函數(shù)上添加SavedStateHandle參數(shù)即可。其他不變
  class HiViewModel(val savedState: SavedStateHandle) : ViewModel() {
    private val KEY_HOME_PAGE_DATA="key_home_page_data"
    private val initData = MutableLiveData<List<GoodsModel>>()
    fun loadInitData():LiveData<List<GoodsModel>> {
       if(initData.value==null){
            //1.from memory .
            //加載數(shù)據(jù)的時(shí)候,先從savedState中嘗試讀取,如果有直接內(nèi)存復(fù)用
            val memoryData =savedState.get<List<GoodsModel>>(KEY_HOME_PAGE_DATA)
            if(memoryData!=null){
             initData.postValue(memoryData)
          }else{
             //2.from remote
             val remoteData = fetchDataFromRemote()
             //然后存儲(chǔ)在savedState以備不時(shí)之需,數(shù)據(jù)模型需要使用parceable接口
             //這種寫法,即便頁(yè)面因配置變更,內(nèi)存不足被回收
             //頁(yè)面重建后,我們都能第一時(shí)間復(fù)用之前的數(shù)據(jù),從而快速渲染頁(yè)面
             //這種能力,對(duì)于一級(jí)頁(yè)面尤其首頁(yè)是至為重要的
             savedState.set(KEY_HOME_PAGE_DATA,remoteData)
             initData.postValue(remoteData)
          }
        }
      return initData
    }
  }

問(wèn)題是:我們?yōu)槭裁纯梢灾苯釉赩iewModel構(gòu)造函數(shù)上直接添加SavedStateHandle參數(shù)呢?這個(gè)問(wèn)題我們后面回答。

SavedState數(shù)據(jù)存儲(chǔ)&復(fù)用實(shí)現(xiàn)原理
從上面介紹上來(lái)理解的話,我認(rèn)為SavedState還是比較簡(jiǎn)單,思路清晰的,但是谷歌搞了一堆設(shè)計(jì)模式,這個(gè)流程原理會(huì)涉及到幾個(gè)類。所以先捋一捋各種關(guān)系,以及它們各自的職責(zé):

  • SavedStateRegistryOwner:用于獲取SavedStatedRegistry對(duì)象;
  • SavedStateRegistryController:用于創(chuàng)建SavedStatedRegistry,用于連接Activity/Fragment和SavedStateRegistry;
  • SavedStateRegistry:數(shù)據(jù)存儲(chǔ)、恢復(fù)中心;
  • SavedStateHandleController: 用于從數(shù)據(jù)中心提出數(shù)據(jù)并創(chuàng)建SavedStateHandle的;
  • SavedStateHandle:單個(gè)ViewModel用于數(shù)據(jù)存儲(chǔ)和恢復(fù)的地方。
SavedStateRegistry模型

一個(gè)總Bundle,以key-value形式存儲(chǔ)著每個(gè)ViewModel對(duì)應(yīng)的子Bundle。這是為了方便整存整取。


SavedState數(shù)據(jù)存儲(chǔ)流程

逐一調(diào)用每個(gè)SavedStateHandle保存自己的數(shù)據(jù)。匯總成一個(gè)總的Bundle,再存儲(chǔ)到Activity的SavedState對(duì)象中。



SavedStateHandle參數(shù)是如何被傳遞到ViewModel的?

現(xiàn)在我們來(lái)回答剛才提出的問(wèn)題,為什么可以直接在ViewModel構(gòu)造函數(shù)上直接添加SavedStateHandle參數(shù)呢?,這里分成兩部,先看下面兩張流程圖。

SavedState數(shù)據(jù)復(fù)用流程

從Activity的onCreate(Bundle savedState)恢復(fù)所有ViewModel的數(shù)據(jù)到SavedStateRegistry。這一部只是提取出存儲(chǔ)的Bundle數(shù)據(jù)對(duì)象,但還沒(méi)有被復(fù)用。

SavedState數(shù)據(jù)復(fù)用流程(2)

創(chuàng)建ViewModel實(shí)例的時(shí)候會(huì)從SavedStateRegistry獲取當(dāng)前ViewModel之前存儲(chǔ)的數(shù)據(jù)并賦值給SavedStateHandle對(duì)象。并將SavedStateHandle以參數(shù)的形式傳遞到ViewModel完成復(fù)用。

接下來(lái)是源碼分析,這一切還要從ViewModelProvider構(gòu)造函數(shù)說(shuō)起:

class ViewModelProvider{
      public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
      //這里他會(huì)判斷我們的Activity/Fragment是不是 HasDefaultViewModelProviderFactory類型的
      //而恰巧我們的Activity/Fragment都是實(shí)現(xiàn)了這個(gè)接口的,并且返回的都是SavedStateViewModelFactory實(shí)例對(duì)象
      //所以,當(dāng)我們調(diào)用這個(gè)構(gòu)造函數(shù)創(chuàng)建ViewModelProvider,來(lái)獲取ViewModel的時(shí)候
      //都是由SavedStateViewModelFactory負(fù)責(zé)具體的創(chuàng)建以及參數(shù)傳遞的。
            this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
}

getDefaultViewModelProviderFactory默認(rèn)返回SavedStateViewModelFactory

class ComponentActivity extends HasDefaultViewModelProviderFactory{
 public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }
}

SavedStateViewModelFactory:創(chuàng)建ViewModel實(shí)例的工作

public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
//1.首先判斷我們的viewmodel 是不是 AndroidViewModel的子類
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor<T> constructor;
        if (isAndroidViewModel) {
          //2.如果是AndroidViewModel的子類,那么嘗試查找它是否擁有(Application,SavedStateHandle)兩個(gè)參數(shù)的構(gòu)造函數(shù)
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            //3.否則查找它是否擁有(SavedStateHandle)一個(gè)參數(shù)的構(gòu)造函數(shù)
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // doesn't need SavedStateHandle
        if (constructor == null) {
            //4. 如果上面兩種方式都沒(méi)找到,說(shuō)明我們的viewmodel沒(méi)有聲明以上兩種類型    
            //此時(shí)這里使用AndroidViewModelFactory創(chuàng)建viewmodel實(shí)例
            //可以無(wú)參數(shù),也可以攜帶一個(gè)(Application)參數(shù)
            return mFactory.create(modelClass);
        }

        //5. 走到這里,那么就說(shuō)明構(gòu)造函數(shù)上存在savedSateHandle參數(shù)
        //這里會(huì)首先構(gòu)建出SavedStateHandleController對(duì)象
        //這個(gè)類的作用是 從SavedStateRegistry提取出該ViewModel的Bundle數(shù)據(jù)對(duì)象,創(chuàng)建savedSateHandle對(duì)象
        SavedStateHandleController controller = SavedStateHandleController.create(
                mSavedStateRegistry, mLifecycle, key, mDefaultArgs);

            T viewmodel;
            if (isAndroidViewModel) {
                //這里反射構(gòu)造ViewModel實(shí)例對(duì)象的時(shí)候,把參數(shù)一同傳遞了進(jìn)去。 
                viewmodel = constructor.newInstance(mApplication, controller.getHandle());
            } else {
            //和上面唯一的不同就是沒(méi)有Application參數(shù)。
                viewmodel = constructor.newInstance(controller.getHandle());
            }
             //返回新創(chuàng)建的ViewModel實(shí)例對(duì)象
             return viewmodel;
    }

SavedStateHandleController提取舊數(shù)據(jù)并創(chuàng)建SavedStateHandle:

static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,
            String key, Bundle defaultArgs) {
        //從數(shù)據(jù)中心SavedStateRegistry提出出該ViewModel的Bundle數(shù)據(jù)對(duì)象    
        Bundle restoredState = registry.consumeRestoredStateForKey(key);
        //構(gòu)建SavedStateHandle對(duì)象,并把restoredState中的數(shù)據(jù)再拆分出來(lái),存儲(chǔ)到mRegular這個(gè) map集合中。
        SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
        SavedStateHandleController controller = new SavedStateHandleController(key, handle);
        //.....
        return controller;
    }
SavedStateHandle getHandle() {
        return mHandle;
    }
總結(jié)
  • ViewModel搭配SavedState組件,可以實(shí)現(xiàn)非正常關(guān)閉情況的數(shù)據(jù)存儲(chǔ)與復(fù)用,這對(duì)于一級(jí)頁(yè)面,尤其是首頁(yè)模塊及其重要。
  • SavedState本質(zhì)是利用了onSaveInstanceState的時(shí)機(jī)。每個(gè)ViewModel的數(shù)據(jù)單獨(dú)存儲(chǔ)在一個(gè)Bundle中,再合并成一個(gè)整體。再存放到outBundle中。所以它也不能存超過(guò)1M的數(shù)據(jù)。
最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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