JetPack 之 ViewModel

image.png

Q:ViewModel是什么?
ViewModel是MVVM架構(gòu)的一個(gè)層級(jí),用來(lái)聯(lián)系View和model之間的關(guān)系。

官方文檔解釋:

ViewModel 類旨在以注重生命周期的方式存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù)。

  • 注重生命周期的方式

由于ViewModel的生命周期是作用于整個(gè)Activity的,所以就節(jié)省了一些關(guān)于狀態(tài)維護(hù)的工作,最明顯的就是對(duì)于屏幕旋轉(zhuǎn)這種情況,以前對(duì)數(shù)據(jù)進(jìn)行保存讀取,而ViewModel則不需要,他可以自動(dòng)保留數(shù)據(jù)。

其次,由于ViewModel在生命周期內(nèi)會(huì)保持局部單例,所以可以更方便Activity的多個(gè)Fragment之間通信,因?yàn)樗麄兡塬@取到同一個(gè)ViewModel實(shí)例,也就是數(shù)據(jù)狀態(tài)可以共享了。

  • 存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù)

ViewModel層的根本職責(zé),就是負(fù)責(zé)維護(hù)界面上UI的狀態(tài),其實(shí)就是維護(hù)對(duì)應(yīng)的數(shù)據(jù),因?yàn)閿?shù)據(jù)會(huì)最終體現(xiàn)到UI界面上。所以ViewModel層其實(shí)就是對(duì)界面相關(guān)的數(shù)據(jù)進(jìn)行管理,存儲(chǔ)等操作。

Q:請(qǐng)談?wù)勈褂肰iewModel的好處?
ViewModel類旨在以注重生命周期的形式存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù)。

1.ViewModel類讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)留存。

2.界面控制器經(jīng)常需要進(jìn)行需要一些時(shí)間才會(huì)返回的異步調(diào)用 ,界面控制器需要管理這些調(diào)用,并確保系統(tǒng)在其銷毀后清理這些調(diào)用以避免潛在的內(nèi)存泄漏。此項(xiàng)管理需要大量的維護(hù)工作,并且在為配置更改重新創(chuàng)建對(duì)象的情況下,會(huì)造成資源的浪費(fèi),因?yàn)閷?duì)象可能需要重新發(fā)出已經(jīng)發(fā)出過(guò)的調(diào)用。

3.諸如 Activity 和 Fragment 之類的界面控制器主要用于顯示界面數(shù)據(jù)、對(duì)用戶操作做出響應(yīng)或處理操作系統(tǒng)通信(如權(quán)限請(qǐng)求)。如果要求界面控制器也負(fù)責(zé)從數(shù)據(jù)庫(kù)或網(wǎng)絡(luò)加載數(shù)據(jù),那么會(huì)使類越發(fā)膨脹。為界面控制器分配過(guò)多的責(zé)任可能會(huì)導(dǎo)致單個(gè)類嘗試自己處理應(yīng)用的所有工作,而不是將工作委托給其他類。以這種方式為界面控制器分配過(guò)多的責(zé)任也會(huì)大大增加測(cè)試的難度。

從界面控制器邏輯中分離出視圖數(shù)據(jù)所有權(quán)的操作更容易且更高效。

Q:為什么不能在ViewModel中引用view、Lifecycle或任何可能持有Activity Context的類?

ViewModels存在的時(shí)間比View或LifecycleOwners的特定實(shí)例存在的時(shí)間更長(zhǎng)——如果您將Activity旋轉(zhuǎn)3次,則您剛剛創(chuàng)建了三個(gè)不同的Activity實(shí)例,但是只有一個(gè)ViewModel實(shí)例。

如果 ViewModel 需要 Application 上下文(例如,為了查找系統(tǒng)服務(wù)),它可以擴(kuò)展 AndroidViewModel 類并設(shè)置用于接收 Application 的構(gòu)造函數(shù),因?yàn)?Application類會(huì)擴(kuò)展 Context。

Q:ViewModel是否對(duì)數(shù)據(jù)使用了持久化?

ViewModel 持有 UI 中的臨時(shí)數(shù)據(jù),但是他們不會(huì)進(jìn)行持久化。一旦相關(guān)聯(lián)的 UI 控制器(fragment/activity)被銷毀或者進(jìn)程停止了,ViewModel 和所有被包含的數(shù)據(jù)都將被垃圾回收機(jī)制標(biāo)記。

Q:onSaveInstanceState()和ViewModel的區(qū)別?

onSaveInstanceState() ->這個(gè)回調(diào)是為了保存兩種情況下的少量 UI 相關(guān)的數(shù)據(jù):

  • 應(yīng)用進(jìn)程在后臺(tái)時(shí)由于內(nèi)存限制而被終止
  • 配置更改

onSaveInstanceState() 是被系統(tǒng)在 activity stopped但沒(méi)有 finished 時(shí)調(diào)用的,而不是在用戶顯式地關(guān)閉 activity 或者在其他情形而導(dǎo)致 finish() 被調(diào)用的時(shí)候調(diào)用。
onSaveInstanceState() 不是被設(shè)計(jì)來(lái)存儲(chǔ)類似 bitmap 這樣的大的數(shù)據(jù)的。onSaveInstanceState() 方法被設(shè)計(jì)用來(lái)存儲(chǔ)那些小的與 UI 相關(guān)的并且序列化或者反序列化不復(fù)雜的數(shù)據(jù)。如果被序列化的對(duì)象是復(fù)雜的話,序列化會(huì)消耗大量的內(nèi)存。由于這一過(guò)程發(fā)生在主線程的配置更改期間,它需要快速處理才不會(huì)丟幀和引起視覺(jué)上的卡頓。

onRetainNonConfigurationInstance()->onSaveInstanceState方法保存數(shù)據(jù)到Bundle,Bundle是有類型限制和大小限制的,而且也要在主線程序列化和反序列化數(shù)據(jù),而onRetainNonConfigurationInstance是保存數(shù)據(jù)到Object,類型和大小都沒(méi)有限制。

Fragment.setRetainInstance(true)->創(chuàng)建一個(gè)保留 fragment 的好處是這可以保存類似 image 那樣的大型數(shù)據(jù)集或者網(wǎng)絡(luò)連接那樣的復(fù)雜對(duì)象。

ViewModel->只能在配置更改相關(guān)的銷毀的情況下保留,而不能在被終止的進(jìn)程中存留。這使 ViewModel 成為搭配 setRetainInstance(true)(實(shí)際上,ViewModel 在幕后使用了一個(gè) fragment 并將 setRetainInstance 方法中的參數(shù)設(shè)置為 true) 一塊使用的 fragment 的一種替代品。

image.png

Q:ViewModel實(shí)現(xiàn)機(jī)制包含了什么設(shè)計(jì)模式?

工廠模式:ViewModelProviders 實(shí)際相當(dāng)于一個(gè) 簡(jiǎn)單工廠 模式,根據(jù)不同的參數(shù)構(gòu)造不同的 ViewModelProvider;而 Facotry 則是一個(gè) 工廠方法 模式可以實(shí)現(xiàn)不同的具體 Factory 來(lái)構(gòu)造不同的 ViewModel。

val ViewModel1 = ViewModelProviders.of(activity,factory).get(ViewModel::class.java)
val ViewModel2 = ViewModelProviders.of(fragment,factory).get(ViewModel::class.java)

Q:ViewModel是如何創(chuàng)建的,又是什么時(shí)候被銷毀?

1. 根據(jù)Activity或Fragment使用ViewModelProviders.of方法獲得ViewModelProvider實(shí)例

創(chuàng)建ViewModelProvider實(shí)例需要兩個(gè)參數(shù)ViewModelStoreOwner以及Factory

  • Factory -> 定義了創(chuàng)建ViewModel的行為接口。里面只有一個(gè)create方法,用于子類自行決定如何實(shí)現(xiàn)一個(gè)ViewModel對(duì)象的創(chuàng)建。
public interface Factory {
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
  • ViewModelStoreOwer -> ViewModelStore的持有者,提供了獲取ViewModelStore實(shí)例的方法,ViewModelStore就是個(gè)HashMap,通過(guò)key來(lái)獲取ViewModel對(duì)象。
public interface ViewModelStoreOwner {
    ViewModelStore getViewModelStore();
}

2. 使用ViewModelProvider.get()創(chuàng)建需要的ViewModel

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

        if (modelClass.isInstance(viewModel)) {
            return (T) viewModel;
        }
        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }
  • 使用ViewModel Class的canonicalName作為ViewModel在ViewModelStore中的唯一標(biāo)識(shí)。
  • 通過(guò)唯一標(biāo)識(shí),先查詢一下ViewModelStore中是否有該ViewModel對(duì)象,如果有則直接返回。
  • 如果ViewModelStore中沒(méi)有該ViewModel對(duì)象,則通過(guò)Factory工廠類mFactory.create反射創(chuàng)建出ViewModel對(duì)象,存入ViewModelStore中,并返回給調(diào)用者。

這里如果沒(méi)有傳入Factory則會(huì)判斷ViewModel類型是否是AndroidViewModel,如果是,則使用默認(rèn)的AndroidViewModelFactory,如果不是則使用AndroidViewModelFactory的父類NewInstanceFactory,即調(diào)用Class的無(wú)參構(gòu)造函數(shù)創(chuàng)建ViewModel對(duì)象

Q: ViewModel的銷毀

  • Activity中的銷毀

在ComponentActivity的構(gòu)造方法中,可以看到通過(guò)Lifecycle在ON_DESTROY事件中銷毀ViewModel。

public ComponentActivity() {
    getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        // 銷毀ViewModel
                        getViewModelStore().clear();
                    }
                }
            }
        });
 }
  • Fragment中的銷毀

首先通過(guò)代碼跟蹤到ViewModelStore的clear()方法調(diào)用的地方,在FragmentManagerViewModel類的clearNonConfigState()方法中找到了ViewModel的銷毀邏輯。
如果繼續(xù)跟蹤代碼可以看到代碼的調(diào)用棧是 FragmentStateManager::destroy() -> (Fragment狀態(tài)切換)->FragmentManager::dispatchDestroy()->FragmentActivity::onDestory()。

void clearNonConfigState(@NonNull Fragment f) {
    ...
    // Clear and remove the Fragment's ViewModelStore
    ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
    if (viewModelStore != null) {
        // 銷毀ViewModel
        viewModelStore.clear();
        mViewModelStores.remove(f.mWho);
    }
}

Q:為什么不同的Fragment使用相同的Activity對(duì)象來(lái)獲取ViewModel,可以輕易的實(shí)現(xiàn)ViewModel共享?

它們其實(shí)共享的是Activity的ViewModel。因?yàn)椴煌腇ragment使用相同的Activity對(duì)象來(lái)獲取ViewModel,在創(chuàng)建ViewModel之前都會(huì)先從Activity提供的ViewModelStore中先查詢一遍是否已經(jīng)存在該ViewModel對(duì)象。
實(shí)現(xiàn)的邏輯在Fragment的activityViewModel()中:

inline fun <reified VM : ViewModel> Fragment.activityViewModels(
    noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })

在activityViewModels()的實(shí)現(xiàn)中可以看到是requireActivity()獲取的viewModelStore。以此來(lái)實(shí)現(xiàn)共享ViewModel。

Q:ViewModel為什么可以實(shí)現(xiàn)屏幕旋轉(zhuǎn)而不銷毀?

1.Activity實(shí)現(xiàn)了ViewModelStoreOwner接口,創(chuàng)建了ViewModelStore對(duì)象。

public ViewModelStore getViewModelStore() {
        ...
        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;
    }

2.當(dāng)Activity意外銷毀時(shí),onRetainNonConfigurationInstance函數(shù)被回調(diào),在此函數(shù)中對(duì)ViewModelStore對(duì)象進(jìn)行了保存。

public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }

3.當(dāng)Activity重建時(shí),onCreate方法中會(huì)先獲取getLastNonConfigurationInstance,如果其中的ViewModelStore對(duì)象不為空,就直接引用,不再重新創(chuàng)建ViewModelStore對(duì)象了。

ViewModel 和 LiveData:模式 + 反模式

ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders

字節(jié)高工面試靈魂7問(wèn):Android架構(gòu)組件—ViewModel這些知識(shí)點(diǎn)一定要掌握!

Android mvvm框架之ViewModel原理

最后編輯于
?著作權(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)容