Jetpack 源碼分析(三) - ViewModel源碼分析

??最初贺嫂,我認(rèn)為ViewModel的原理是非常簡單,包括網(wǎng)絡(luò)上有很多相關(guān)的源碼分析的文章都是這么認(rèn)為梁呈。但是當(dāng)我再一次認(rèn)認(rèn)真真的官方文檔之后颅围,才知道ViewModel涉及到的東西遠(yuǎn)不及我們所知道的那些。
??眾所周知裹芝,ViewModel保存的數(shù)據(jù)部逮,在配置更改之后依然也是存在的。但是這就是引出了一個(gè)問題嫂易,ViewModelonSaveIntanceState方法有什么區(qū)別甥啄?因?yàn)槲覀兌贾?code>onSaveIntanceState方法就是用來保存和恢復(fù)UI的狀態(tài)和數(shù)據(jù)的。這個(gè)是本文分析的一個(gè)重點(diǎn)炬搭。
??同時(shí),ViewModel只會(huì)保存因?yàn)榕渲酶膶?dǎo)致Activity重建相關(guān)的數(shù)據(jù)穆桂,并不能保存因?yàn)锳ctivity退到后臺(tái)宫盔,因資源限制(比如內(nèi)存限制,電量限制等)導(dǎo)致被殺掉之前的數(shù)據(jù)--這就是onSaveIntanceState方法干的事享完。但是如果我們想要ViewModel能夠擁有這部分的能力灼芭,又應(yīng)該怎么辦呢?這就設(shè)計(jì)到SavedStateHandle般又,這也是本文就要分析的一個(gè)重點(diǎn)彼绷。特別注意的是,我大概看了一下網(wǎng)絡(luò)上相關(guān)的資料茴迁,發(fā)現(xiàn)在關(guān)于這部分的資料特別的少寄悯,大家似乎都不太重視這一塊知識(shí),但是我認(rèn)為這一塊的知識(shí)特別有必要介紹一下堕义,所以本文會(huì)花費(fèi)很多的筆墨分析它猜旬。
??本文參考資料:

  1. ViewModel 概覽
  2. ViewModel 的已保存狀態(tài)模塊
  3. ViewModels : A Simple Example
  4. ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders
  5. Architecture SavedStateHandle — the ViewModel’s complement
  6. Android ViewModels: State persistence — SavedState

??注意:本文ViewModel相關(guān)源碼均是2.2.0版本。

1. 概述

??按照老規(guī)矩,我們先來介紹我們對ViewModel已有的認(rèn)識(shí)吧洒擦。在MVVM中椿争,我們都知道ViewModel承擔(dān)著一個(gè)重要角色--數(shù)據(jù)持有者。這個(gè)怎么來理解呢熟嫩?我們用一個(gè)具體的場景來看看秦踪。
??假設(shè)一個(gè)Activity界面有一個(gè)RecyclerView正在展示,同時(shí)RecyclerView的數(shù)據(jù)是從服務(wù)端請求過來的掸茅。當(dāng)我們的手機(jī)第一次進(jìn)入這個(gè)界面椅邓,手機(jī)是豎屏的,數(shù)據(jù)正常的請求倦蚪,RecyclerView拿到數(shù)據(jù)也正常的展示希坚。但是此時(shí),如果我此時(shí)將手機(jī)橫屏的話陵且,會(huì)怎么樣呢裁僧?會(huì)經(jīng)歷如下幾個(gè)過程:

  1. 原來的Activity對象正常的走生命周期進(jìn)行銷毀。
  2. 新的Activity會(huì)被創(chuàng)建慕购,同時(shí)生命周期也會(huì)走到onResume聊疲。
  3. 數(shù)據(jù)的恢復(fù)。如果我們沒有使用ViewModel用來存儲(chǔ)數(shù)據(jù)沪悲,那么就會(huì)重新從服務(wù)端請求數(shù)據(jù)获洲;如果我們ViewModel來存儲(chǔ)數(shù)據(jù),則不會(huì)重新請求數(shù)據(jù)殿如,而是將豎屏前數(shù)據(jù)保存贡珊,并且成功恢復(fù)到橫屏的Activity上去。

??我們可以用官方的一張圖來簡單的描述一下這個(gè)過程:


??從這張圖里面涉馁,我們可以看出门岔,ViewModel的生命周期要比Activity長一點(diǎn)。因?yàn)榕渲酶膶?dǎo)致的Activity銷毀烤送,ViewModel不會(huì)跟著銷毀寒随,只有Activity正常finish(比如用戶點(diǎn)擊back退出,或者退到后臺(tái)殺掉App等)帮坚,ViewModel才會(huì)銷毀妻往。
??上面說的都是大家都知道的知識(shí),本文會(huì)從源碼角度去分析為什么ViewModel能實(shí)現(xiàn)這種效果试和。
??同時(shí)還會(huì)介紹SavedStateHandle讯泣,這個(gè)也在前面也說過了。因?yàn)槲覀兌贾阑沂穑琕iewModel不能保存Activity被系統(tǒng)殺掉然后重建的數(shù)據(jù)判帮,而借助SavedStateHandle,ViewModel便能解決這個(gè)問題局嘁。說句題外話,估計(jì)是Google看到大家怨聲載道---為啥ViewModel只能保存配置更改的數(shù)據(jù)晦墙,才隨便實(shí)現(xiàn)這個(gè)類來滿足大家悦昵。正所謂大家苦ViewModel久矣。

2. 基本使用

??在正式分析源碼之前晌畅,我們先來看看ViewModel是怎么使用(雖然大家都比較熟悉)但指。首先,我們創(chuàng)建一個(gè)ViewModel子類抗楔,類里面有一個(gè)LiveData對象:

class MyViewModel : ViewModel() {
    val mNameLiveData = MutableLiveData<String>()
}

??然后我們在Activity里面使用它:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val textView = findViewById<TextView>(R.id.textView)
        val viewModel = ViewModelProvider(this)[MyViewModel::class.java]
        viewModel.mNameLiveData.observe(this, Observer {
            textView.text = it
        })
    }
}

??我們的例子非常簡單棋凳,這里就不過多的介紹。需要提一句的是连躏,在最新的ViewModel中剩岳,以前通過ViewModelProviders.of方法來獲取ViewModel已經(jīng)過時(shí)了,現(xiàn)在我們是通過ViewModelProvider方法創(chuàng)建ViewModel對象入热。通常來說拍棕,我們還需要往ViewModelProider構(gòu)造方法里面?zhèn)鬟f一個(gè)工廠類對象,如下:

        ViewModelProvider(this, object : ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>) =
                MyViewModel() as T
        })

??當(dāng)然勺良,我們可以不帶Factory對象绰播。那么加入Factory對象之后,相比較于以前有什么好處呢尚困?加了Factory之后蠢箩,我們可以定義構(gòu)造方法帶參的ViewModel。比如說事甜,如果谬泌,我們的一個(gè)ViewModel構(gòu)造方法需要帶一個(gè)id參數(shù),那么我們可以在Factory的create方法里面創(chuàng)建對象直接帶進(jìn)去逻谦。
??切忌呵萨,我們不要自己創(chuàng)建ViewModel對象,因?yàn)樽约簞?chuàng)建的對象不能保存因?yàn)榕渲酶膶?dǎo)致Activity重建的數(shù)據(jù)跨跨,從而完美避開了ViewModel的優(yōu)點(diǎn)。
??針對于ViewModel的源碼分析囱皿,本文從Factory這個(gè)切入點(diǎn)入手勇婴,逐步的分析ViewModel的創(chuàng)建和恢復(fù)。

2. ViewModel的創(chuàng)建

??首先嘱腥,我們先來看ViewModelProivder的構(gòu)造方法耕渴。ViewModelProivder有很多構(gòu)造方法,不過最終都調(diào)到同一個(gè)地方:

    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

??從這個(gè)方法中齿兔,我們看到兩個(gè)變量橱脸,mFactory就是我們預(yù)期的工廠類础米,用來創(chuàng)建ViewModel對象;mViewModelStore是一個(gè)什么東西呢添诉?這個(gè)很好理解屁桑,mViewModelStore就是用來存儲(chǔ)的ViewModel對象的,比如同一個(gè)Activity的onCreate方法可能會(huì)多次回調(diào)栏赴,我們在onCreate方法初始化ViewModel的蘑斧,但是不可能每次onCreate回調(diào)都會(huì)創(chuàng)建新的ViewModel對象,所以需要有一個(gè)東西用來存儲(chǔ)的我們之前創(chuàng)建過的ViewModel须眷,這個(gè)就是ViewModelStore的作用竖瘾。同時(shí)在后面的內(nèi)容,ViewModel生命周期比Activity的生命周期長也是因?yàn)檫@個(gè)類花颗。
??那么mViewModelStore對象是從哪里傳過來捕传,我們清楚的記得構(gòu)造方法里面我們并沒有傳這個(gè)變量。

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }

??我們可以看到從ViewModelStoreOwner獲取的扩劝,那么那么些類是這個(gè)借口的實(shí)現(xiàn)類呢庸论?從類圖上來看,我們熟悉的ComponentActivity和Fragment實(shí)現(xiàn)了這個(gè)接口(這里我說的都是androidX包下)今野。這里葡公,先不對ViewModelStoreOwner進(jìn)行展開,后面在分析ViewModel的恢復(fù)會(huì)詳細(xì)的講解条霜。
??總之催什,上面兩個(gè)變量都非常的重要。
??我們再來看一下get方法宰睡,因?yàn)檎嬲@取ViewModel對象就是通過這個(gè)方法的蒲凶。

    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }

??這個(gè)get方法沒有做什么事情,構(gòu)造了一個(gè)默認(rèn)的key拆内,然后調(diào)用另一個(gè)get方法旋圆。我們來看看:

    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

??這個(gè)get方法總的來說,主要分為以下2個(gè)過程:

  1. 先通過key從ViewModelStore(緩存)獲取ViewModel對象麸恍,如果緩存中存在灵巧,直接返回。Activity經(jīng)過橫屏重建之后抹沪,返回ViewMode的對象l就是這里返回刻肄。
  2. 如果緩存不存在,那么通過Factory創(chuàng)建一個(gè)對象融欧,然后放在緩存中敏弃,最后返回。

??默認(rèn)的Facotry噪馏,我們這里不用過多的解釋麦到,就是簡單的調(diào)用create方法創(chuàng)建ViewModel對象咸这。那么那個(gè)OnRequeryFactoryKeyedFactory又是一個(gè)什么東西呢检眯?KeyedFactory跟默認(rèn)的Facotry比較虚汛,其實(shí)就是create方法多了一個(gè)參數(shù)痛垛,這個(gè)不用過多的糾結(jié)。
??這里的核心是OnRequeryFactory步清,我們發(fā)現(xiàn)要门,當(dāng)ViewModel是從ViewModelStore獲取的,就是回調(diào)一次OnRequeryFactoryonRequery方法廓啊。這里回調(diào)onRequery方法有什么作用呢欢搜?這個(gè)設(shè)計(jì)到后面的SavedStateHandle,我們都知道當(dāng)Activity因?yàn)橘Y源限制被系統(tǒng)殺掉之后重新創(chuàng)建會(huì)恢復(fù)之前的狀態(tài)谴轮,這里的onRequery方法的作用就是--為在這中情況能保存數(shù)據(jù)做做準(zhǔn)備(往SavedStateRegistry里面注冊SavedStateProvider)炒瘟。那么可能有人就要問了ViewModel沒有從緩存獲取ViewModel,為啥不回調(diào)onRequery方法呢第步?我想說的是疮装,注冊最后都是要做的是,只是做工作可能不太一樣粘都,所以看不到onRequery方法的回調(diào)廓推。這里,我先粗略描述一下翩隧,后面的內(nèi)容會(huì)詳細(xì)的分析這一塊的內(nèi)容樊展,大家先對此有一個(gè)印象就行。

3. ViewModel的恢復(fù)

??在前面的概述中堆生,我們已經(jīng)知道ViewModel的生命周期要比Activity長一點(diǎn)专缠。那ViewModel是怎么做到的呢?對于這個(gè)問題淑仆,我猜大家首先想到的是緩存涝婉,并且這個(gè)緩存是被static關(guān)鍵字修飾的。正常來說蔗怠,這個(gè)實(shí)現(xiàn)方案是沒有問題的墩弯,我們也能找到具體的例子,比如Eventbus就是這么實(shí)現(xiàn)的寞射。
??那么在ViewModel中最住,這個(gè)是怎么實(shí)現(xiàn)的呢?我們都知道ViewModel是從一個(gè)ViewModelStore緩存里面的獲取怠惶,我們看了ViewModelStore的源碼,發(fā)現(xiàn)它的內(nèi)部并沒有通過靜態(tài)緩存實(shí)現(xiàn)轧粟。那么它是怎么實(shí)現(xiàn)Activity在onDestroy之后(重建)策治,還繼續(xù)保留已有的對象呢脓魏?
??這個(gè)我們可以從ComponentActivitygetViewModelStore方法去尋找答案:

    public ViewModelStore getViewModelStore() {
        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 (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

??getViewModeStrore方法的目的很簡單,就是獲取一個(gè)ViewModelStrore對象通惫。那么這個(gè)ViewModelStore可以從哪里獲取呢茂翔?我們從上面的代碼中可以找到兩個(gè)地方:

  1. 從NonConfigurationInstances獲取。
  2. 創(chuàng)建一個(gè)新的ViewModelStore對象履腋。

??第二點(diǎn)我們不用看珊燎,關(guān)鍵是NonConfigurationInstancesNonConfigurationInstances這是什么東西遵湖?這里悔政,我簡單解釋一下NonConfigurationInstances

NonConfigurationInstances其實(shí)就是一個(gè)Wrapper,用來包裝一下因?yàn)椴皇芘渲酶挠绊懙臄?shù)據(jù)延旧,包括我們非常熟悉的Fragment谋国,比如說,一個(gè)Activity上面有一個(gè)Fragment迁沫,旋轉(zhuǎn)了屏幕導(dǎo)致Activity重新創(chuàng)建芦瘾,此時(shí)Activity跟之前的不是同一個(gè)對象,但是Fragment卻是同一個(gè)集畅。這就是通過NonConfigurationInstances實(shí)現(xiàn)的(有興趣的同學(xué)可以了解一下Fragment這個(gè)case近弟,還挺重要的,這里就不解釋了挺智。)祷愉。

??上面說的內(nèi)容看上去跟我們想要的效果挺像的。是的逃贝,ViewModelStore在Activity重建前后能保持同一個(gè)對象就是通過NonConfigurationInstances實(shí)現(xiàn)的谣辞。也就是說在getViewModelStore方法里面,從NonConfigurationInstances獲取的ViewModelStore對象其實(shí)就是上一個(gè)Activity的沐扳。同時(shí)泥从,我們還可以在ComponentActivity里面看到一段代碼:

        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });

??從上面的代碼中,我們可以到如果Activity是因?yàn)榕渲酶膶?dǎo)致onDestroy方法的回調(diào)沪摄,并不會(huì)清空ViewModelStore里面的內(nèi)容躯嫉,這就能保證當(dāng)Activity因?yàn)榕渲酶膶?dǎo)致重建重新創(chuàng)建的ViewModel對象跟之前創(chuàng)建的對象是同一個(gè)。反之杨拐,如果Activity是正常銷毀的話祈餐,則不會(huì)保存之前創(chuàng)建的ViewModel對象,對應(yīng)的是ViewModelStore的clear方法調(diào)用哄陶。其實(shí)這個(gè)clear方法還跟kotlin里面的協(xié)程有關(guān)帆阳,這里就不過多解釋了,有興趣的同學(xué)可以看看ViewModel.viewModelScope屋吨。
??現(xiàn)在我們來看一下NonConfigurationInstances為啥能保證Activity重建前后蜒谤,ViewModeStore是同一個(gè)對象呢山宾?主要滿足大家的好奇心。
??我們直接從ActivityThreadperformDestroyActivity方法去尋找答案(是不是很慌)鳍徽。我們知道资锰,performDestroyActivity方法最后會(huì)回調(diào)到Activity的onDestroy方法,我們可以通過這個(gè)方法可以找到ActivtyThread在Activity onDestroy之前做了保存操作阶祭。

    ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        // ······
        performPauseActivityIfNeeded(r, "destroy");
        // Activity的onStop方法回調(diào)
        if (!r.stopped) {
            callActivityOnStop(r, false /* saveState */, "destroy");
        }
        if (getNonConfigInstance) {
            // ······
            // retainNonConfigurationInstances方法的作用就是創(chuàng)建一個(gè)對象
            r.lastNonConfigurationInstances= r.activity.retainNonConfigurationInstances();
            // ······
        }
        // ······
        // Activity的onDestroy方法回調(diào)
        mInstrumentation.callActivityOnDestroy(r.activity);
        // ······
        return r;
    }

??從上面的代碼中绷杜,我們可以看到,在Activity的onStop和onDestroy之間濒募,會(huì)回調(diào)retainNonConfigurationInstances方法鞭盟,同時(shí)記錄到ActivityClientRecord中去。這里retainNonConfigurationInstances方法返回的對象就是我們之前看到的NonConfigurationInstances對象萨咳。
??那么又在哪里恢復(fù)已保存的NonConfigurationInstances對象呢懊缺?這個(gè)可以從performLaunchActivity方法找到答案。performLaunchActivity方法的作用就是啟動(dòng)一個(gè)Activity培他,Activity重建肯定會(huì)調(diào)用這個(gè)方法鹃两。在performLaunchActivity方法里面,調(diào)用了Activity的attach方法舀凛,在這個(gè)方法俊扳,Google將已有的NonConfigurationInstances賦值給了新的Activity對象。
??到這里猛遍,我們就知道為啥NonConfigurationInstances能保證ViewModelStore在Activity重建前后是同一個(gè)對象馋记,同時(shí)也知道為啥ViewModel的生命周期比Activity的生命周期要長一點(diǎn)。這里我先不總結(jié)懊烤,為了方便大家理解梯醒,最后我會(huì)統(tǒng)一的總結(jié)。

4. SavedStateHandle

??通過前面的內(nèi)容腌紧,我們可以知道ViewModel保存和恢復(fù)的數(shù)據(jù)范圍僅限于配置更改導(dǎo)致的重建茸习,并不支持因?yàn)橘Y源限制導(dǎo)致Activity重建(或許不應(yīng)該稱為重建?)的情況壁肋。但是号胚,大家對此的呼聲卻從來沒有停歇,Google因此新增了一個(gè)SavedStateHandle類浸遗,用來滿足我們的要求猫胁。
??在正式介紹SavedStateHandle之前,我們先看看onSaveInstanceStateViewModel之間的區(qū)別跛锌。

(1). onSaveInstanceState和ViewModel的區(qū)別

??我們先來看看onSaveInstanceState:

  1. onSaveInstanceState方法能保存的數(shù)據(jù)的場景:1. 由于資源限制弃秆,Activity被系統(tǒng)殺掉;2. 配置更改。
  2. onSaveInstanceState方法只能保存少量簡單的數(shù)據(jù)菠赚,大量和復(fù)雜數(shù)據(jù)最后不要放在onSaveInstanceState方法保存盼樟。因?yàn)閛nSaveInstanceState方法是通過Bundle保存數(shù)據(jù),如果數(shù)據(jù)的量太大或者太復(fù)雜锈至,會(huì)阻塞UI線程,從而影響Activity生命周期的執(zhí)行译秦。

??我們在來看看ViewModel:

  1. ViewModel能保存的數(shù)據(jù)的場景:配置更改峡捡。不支持由于限制導(dǎo)致Activity重建的場景。
  2. ViewModel支持保存大量和復(fù)雜的數(shù)據(jù)筑悴,比如說RecyclerView的data们拙。

??從上面的內(nèi)容,我們可以很明顯的看出來各自的缺點(diǎn)和優(yōu)點(diǎn)阁吝。既然Google爸爸在強(qiáng)力推薦View層和Model層分離的原則砚婆,那么ViewModel支持限制導(dǎo)致Activity重建的場景就顯得尤為重要了。這不SavedStateHandle就出來了突勇。

(2). SavedStateHandle的基本使用

??在分析SavedStateHandle源碼之前装盯,我們先來看看SavedStateHandle是怎么使用的吧。
??首先定義一個(gè)構(gòu)造方法帶SavedStateHandle參數(shù)的ViewModel:

class MyViewModelWithSaveStateHandle(private val saveStateHandle: SavedStateHandle) : ViewModel() {

    // 需要在資源限制導(dǎo)致重建的場景下保存的數(shù)據(jù)
    // 用LiveData暴露甲馋,不能讓外部直接通過LiveData去修改內(nèi)部的值
    val mAgeLiveData: LiveData<Int> = saveStateHandle.getLiveData(KEY_AGE)

    // 普通的數(shù)據(jù)
    val mNameLiveData = MutableLiveData<String>()


    fun setAge(age: Int) {
        saveStateHandle.set(KEY_AGE, age)
    }

    companion object {
        const val KEY_AGE = "key_age";
    }
}

??其次埂奈,通過AbstractSavedStateViewModelFactory或者SavedStateViewModelFactory創(chuàng)建ViewModel對象:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val viewModel = ViewModelProvider(
            this,
            SavedStateViewModelFactory(application, this)
        )[MyViewModelWithSaveStateHandle::class.java]
    }
}

??如果我們的ViewModel構(gòu)造方法只帶一個(gè)SavedStateHandle參數(shù)或者帶有一個(gè)Application參數(shù)和SavedStateHandle參數(shù),可以直接使用SavedStateViewModelFactory定躏。如果構(gòu)造方法還帶有其他的參數(shù)账磺,此時(shí)需要繼承AbstractSavedStateViewModelFactory實(shí)現(xiàn)我們自己的工廠類。在使用AbstractSavedStateViewModelFactory時(shí)痊远,我們需要注意一點(diǎn):create方法帶的SavedStateHandle參數(shù)一定傳遞到ViewModel里面去垮抗。
??通過上面的2步,我們就可以在ViewModel保存由于資源限制導(dǎo)致重建的數(shù)據(jù)碧聪。是不是很簡單呢冒版?大家是不是如饑似渴的想要了解內(nèi)部實(shí)現(xiàn)原理了呢?
??現(xiàn)在我們準(zhǔn)備分析它的內(nèi)部實(shí)現(xiàn)原理矾削,為啥說是準(zhǔn)備呢壤玫?因?yàn)樵谡娇?code>SavedStateHandle之前,我們先來看看幾個(gè)東西哼凯,我們只有了解完這幾個(gè)東西欲间,分析SavedStateHandle才不會(huì)懵逼。

(3). SavedState components

??我們先來看一下SavedState components里面幾個(gè)組件断部,分別是:SavedStateRegistryOwner猎贴,SavedStateRegistryControllerSavedStateRegistrySavedStateProvider她渴。一下子懟上來4個(gè)不明所以的類达址,我猜測大家多多少少都有點(diǎn)懵逼。在這里我先分別這4個(gè)類的作用:

  1. SavedStateRegistryOwner:一個(gè)接口趁耗,有一個(gè)getSavedStateRegistry方法沉唠,作用是提供SavedStateRegistry對象。該接口主要實(shí)現(xiàn)類有Activity和Fragment苛败。
  2. SavedStateRegistryController:SavedStateRegistry的控制類满葛,主要有兩個(gè)方法:performRestore方法的作用恢復(fù)數(shù)據(jù);performSave方法主要保存數(shù)據(jù)罢屈。Activity和Fragment直接操作類就是該類嘀韧。
  3. SavedStateRegistry: 主要是從UI控制器(Activity或者Fragment等)恢復(fù)數(shù)據(jù),或者需要保存的數(shù)據(jù)寫入U(xiǎn)I控制器的Bundle里面缠捌;外部可以通過registerSavedStateProvider方法注冊SavedStateProvider锄贷,這樣SavedStateRegistry在保存數(shù)據(jù)會(huì)SavedStateProvider提供的數(shù)據(jù)。SavedStateRegistryController主要操作類就是該類曼月。
  4. SavedStateProvider: 主要是提供保存和恢復(fù)的數(shù)據(jù)谊却。該接口只有一個(gè)saveState方法,主要的作用將需要保存的數(shù)據(jù)用Bundle包裝起來十嘿。

??可以用一張圖來描述他們之間的關(guān)系:


??對這4個(gè)類有了大概的認(rèn)識(shí)之后因惭,我們就從源碼的角度來看看這個(gè)4個(gè)類是怎么工作。首先我們來看看ComponentActivity:


public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {

    private final SavedStateRegistryController mSavedStateRegistryController =
            SavedStateRegistryController.create(this)绩衷;
    // ······
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSavedStateRegistryController.performRestore(savedInstanceState);
        // ······
    }

    @CallSuper
    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        // ······
        mSavedStateRegistryController.performSave(outState);
    }
    // ······
}

??我們從ComponentActivity源碼可以看出來蹦魔,SavedStateRegistryController主要分為兩個(gè)過程:

  1. 在onCreate方法調(diào)用了performRestore方法,主要的作用是恢復(fù)已保存的數(shù)據(jù)咳燕。
  2. 在onDestroy方法調(diào)用了performSave方法勿决,主要的作用保存數(shù)據(jù)。

??因此招盲,我們也分為兩個(gè)過程來分析低缩,分別從保存數(shù)據(jù)和恢復(fù)和數(shù)據(jù)來分析。

(A). 保存數(shù)據(jù)

??SvedStateRegistryController的performSave方法做什么曹货,只是調(diào)用了SavedStateRegistry的performSave方法咆繁。我們直接看SavedStateRegistry的performSave方法:

    void performSave(@NonNull Bundle outBundle) {
        Bundle components = new Bundle();
        if (mRestoredState != null) {
            components.putAll(mRestoredState);
        }
        for (Iterator<Map.Entry<String, SavedStateProvider>> it =
                mComponents.iteratorWithAdditions(); it.hasNext(); ) {
            Map.Entry<String, SavedStateProvider> entry1 = it.next();
            components.putBundle(entry1.getKey(), entry1.getValue().saveState());
        }
        outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
    }

??performSave方法里面主要做了三件事:

  1. 如果mRestoredState不為空,表示之前恢復(fù)的數(shù)據(jù)還沒有被消費(fèi)完顶籽,需要將沒有消費(fèi)的數(shù)據(jù)再一次保存玩般。
  2. 遍歷所有注冊的SavedStateProvider,將所有的SavedStateProvider提供的數(shù)據(jù)保存起來礼饱。
  3. 將所有保存的數(shù)據(jù)全部寫入到Activity的Bundle對象坏为。注意:outBundle是Activity需要保存的數(shù)據(jù)Bundle究驴。

??保存的數(shù)據(jù)過程還是比較簡單的,我們再來看看恢復(fù)數(shù)據(jù)的過程匀伏。

(B). 恢復(fù)數(shù)據(jù)

??我們先來看看SvedStateRegistryControllerperformRestore方法:

    public void performRestore(@Nullable Bundle savedState) {
        Lifecycle lifecycle = mOwner.getLifecycle();
        if (lifecycle.getCurrentState() != Lifecycle.State.INITIALIZED) {
            throw new IllegalStateException("Restarter must be created only during "
                    + "owner's initialization stage");
        }
        lifecycle.addObserver(new Recreator(mOwner));
        mRegistry.performRestore(lifecycle, savedState);
    }

??相比于performSave方法洒忧,performRestore多了一些操作。但是我們不需要去關(guān)心够颠,我們的重點(diǎn)還是應(yīng)該放在SavedStateRegistryperformRestore方法:

    void performRestore(@NonNull Lifecycle lifecycle, @Nullable Bundle savedState) {
        if (mRestored) {
            throw new IllegalStateException("SavedStateRegistry was already restored.");
        }
        if (savedState != null) {
            mRestoredState = savedState.getBundle(SAVED_COMPONENTS_KEY);
        }

        lifecycle.addObserver(new GenericLifecycleObserver() {
            @Override
            public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_START) {
                    mAllowingSavingState = true;
                } else if (event == Lifecycle.Event.ON_STOP) {
                    mAllowingSavingState = false;
                }
            }
        });

        mRestored = true;
    }

??SavedStateRegistryperformRestore方法做的操作從savedState獲取之前保存的數(shù)據(jù)熙侍,然后賦值給mRestoredState。到這里履磨,我們可能有疑問了核行?這里恢復(fù)數(shù)據(jù)只看到獲取之前數(shù)據(jù),但是沒看到賦值到ViewModel蹬耘。
??其實(shí)SavedStateRegistry里面還有一個(gè)方法,我們一直沒看--consumeRestoredStateForKey方法减余。我們來看看它的實(shí)現(xiàn):

    public Bundle consumeRestoredStateForKey(@NonNull String key) {
        if (!mRestored) {
            throw new IllegalStateException("You can consumeRestoredStateForKey "
                    + "only after super.onCreate of corresponding component");
        }
        if (mRestoredState != null) {
            Bundle result = mRestoredState.getBundle(key);
            mRestoredState.remove(key);
            if (mRestoredState.isEmpty()) {
                mRestoredState = null;
            }
            return result;
        }
        return null;
    }

??consumeRestoredStateForKey方法的作用就是通過key從mRestoredState獲取保存的數(shù)據(jù)综苔。是不是看到一點(diǎn)苗頭了?不錯(cuò)位岔,外部類就是通過該方法來獲取之前保存的數(shù)據(jù)如筛。

(4). SavedStateViewModelFactory

??在看了4個(gè)類的實(shí)現(xiàn)之后,我們現(xiàn)在回過頭來看看SavedStateViewModelFactory在創(chuàng)建ViewModel做了哪些操作抒抬。我們直接看create方法:

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor<T> constructor;
        if (isAndroidViewModel) {
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // doesn't need SavedStateHandle
        if (constructor == null) {
            return mFactory.create(modelClass);
        }

        SavedStateHandleController controller = SavedStateHandleController.create(
                mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
        try {
            T viewmodel;
            if (isAndroidViewModel) {
                viewmodel = constructor.newInstance(mApplication, controller.getHandle());
            } else {
                viewmodel = constructor.newInstance(controller.getHandle());
            }
            viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
            return viewmodel;
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Failed to access " + modelClass, e);
        } catch (InstantiationException e) {
            throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("An exception happened in constructor of "
                    + modelClass, e.getCause());
        }
    }

??在create方法中杨刨,我們需要注意兩點(diǎn):

  1. 通過SavedStateHandleController的create方法創(chuàng)建了一個(gè)對象。在這個(gè)方法中擦剑,我們能找到很多的問題答案妖胀,比如說將已保存的數(shù)據(jù)恢復(fù)到SavedStateHandle、向SavedStateRegistry中注冊SavedStateProivder以保證能成功保存數(shù)據(jù)惠勒。
  2. 通過反射創(chuàng)建ViewModel的對象赚抡。從這里,我們可以知道為啥SavedStateViewModelFactory的使用受限于ViewModel構(gòu)造方法了纠屋。同時(shí)從反射中可以出來涂臣,將已有數(shù)據(jù)的SavedStateHandle傳遞到ViewModel。這就是能解釋ViewModel為啥加一個(gè)參數(shù)售担,就是保存資源限制導(dǎo)致重建的數(shù)據(jù)的原因赁遗。

??我們再來看看SavedStateHandleController的create方法:

    static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,
            String key, Bundle defaultArgs) {
        Bundle restoredState = registry.consumeRestoredStateForKey(key);
        SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
        SavedStateHandleController controller = new SavedStateHandleController(key, handle);
        controller.attachToLifecycle(registry, lifecycle);
        tryToAddRecreator(registry, lifecycle);
        return controller;
    }

??create方法主要?jiǎng)?chuàng)建一個(gè)SavedStateHandleController對象,除了創(chuàng)建對象之外族铆,還做了其他幾件事:

  1. 調(diào)用consumeRestoredStateForKey方法岩四,獲取之前已保存的數(shù)據(jù)。并且將獲取的數(shù)據(jù)創(chuàng)建一個(gè)SavedStateHandle,以供ViewModel使用骑素。
  2. 調(diào)用attachToLifecycle炫乓。這里做的操作主要是將SavedStateHandleSavedStateProvider注冊到SavedStateRegistry里面刚夺,以保證以下保存數(shù)據(jù)的時(shí)機(jī)能成功SavedStateHandle所支持的數(shù)據(jù)。

??到此末捣,我們將SavedStateHandle里面一個(gè)數(shù)據(jù)者侠姑,它內(nèi)部持有的數(shù)據(jù)主要是從保存和恢復(fù)的,這也是為啥ViewModel需要保存和恢復(fù)的數(shù)據(jù)要通過SavedStateHandle來初始化箩做。

5. 總結(jié)

??到此為止莽红,ViewModel相關(guān)分析就結(jié)束了。在這里邦邦,我對本文做一個(gè)簡單的總結(jié)安吁。

  1. ViewModel 和 onSaveInstaceState方法區(qū)別在于:ViewModel只能保存因?yàn)榕渲酶膶?dǎo)致重建的數(shù)據(jù),但是它能保存大量和復(fù)雜的數(shù)據(jù)燃辖;onSaveInstaceState能保存配置更改導(dǎo)致重建和資源限制導(dǎo)致重建的數(shù)據(jù)鬼店,但是它只能保存少量簡單的數(shù)據(jù)。ViewModel使用SavedStateHandle能夠保存資源限制導(dǎo)致重建的數(shù)據(jù)黔龟。
  2. ViewModel的生命周期之所以比Activity的生命周期生命周期妇智,主要重建之后的Activity用的是之前的ViewStore。ViewModelStore保存的是之前的ViewModel氏身,而ViewStore在配置更改導(dǎo)致重建不會(huì)清空已有ViewModel巍棱。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蛋欣,隨后出現(xiàn)的幾起案子航徙,更是在濱河造成了極大的恐慌,老刑警劉巖陷虎,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件到踏,死亡現(xiàn)場離奇詭異,居然都是意外死亡尚猿,警方通過查閱死者的電腦和手機(jī)夭禽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谊路,“玉大人讹躯,你說我怎么就攤上這事〔埃” “怎么了潮梯?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惨恭。 經(jīng)常有香客問我秉馏,道長,這世上最難降的妖魔是什么脱羡? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任萝究,我火速辦了婚禮免都,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘帆竹。我一直安慰自己绕娘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布栽连。 她就那樣靜靜地躺著险领,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秒紧。 梳的紋絲不亂的頭發(fā)上绢陌,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音熔恢,去河邊找鬼脐湾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛叙淌,可吹牛的內(nèi)容都是我干的沥割。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼凿菩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了帜讲?” 一聲冷哼從身側(cè)響起衅谷,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎似将,沒想到半個(gè)月后获黔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡在验,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年玷氏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腋舌。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡盏触,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出块饺,到底是詐尸還是另有隱情赞辩,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布授艰,位于F島的核電站辨嗽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏淮腾。R本人自食惡果不足惜糟需,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一屉佳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧洲押,春花似錦武花、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至娘荡,卻和暖如春干旁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背炮沐。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工争群, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人大年。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓换薄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翔试。 傳聞我的和親對象是個(gè)殘疾皇子轻要,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355