Jetpack第四篇:ViewModel

1、什么是ViewModel

ViewModel 具備宿主生命后期感知能力的數據存儲組件,可以理解為ViewModel可以用來存儲數據,而且在Activity因為異常銷毀重新創建,依舊存在。這個和Activity的onInstanceSave和onRestoreInstance有點相似。
ViewModel的數據是可以再Activity和Fragment中共享的。

2、ViewModel簡單使用

導入依賴

implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'

對于ViewModel,剛開始學習先去學習它的幾個特性即可。
1、可以在Activity,Fragment中共享數據。
2、可以在多個Activity中共享數據。
3、在異常的生命周期時依舊可以保存數據。

代碼中獲取ViewModel

// 1、初始化ViewModel
var myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)

// 2、初始化ViewModel
var myViewModel = ViewModelProvider(viewModelStore,ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)

以上兩種方式都可以。初始化之后即可使用ViewModel中的數據了。

在Activity和Fragment中共享數據的不同初始化方法
在Activity中使用:

var myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)

this代表activity或者application,如果是Application,那就代表這個viewmodel能在多個Activity中共享了。

ViewModel使用起來比較簡單,示例代碼我寫在最后。一般和LiveData聯用。

3、ViewModel的源碼分析

我們從獲取ViewModel的get方法說起。

    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        // mViewModelStore 可以理解為一個保存VieModel的map,
        // 通過key去獲取這個ViewMode
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            // 如果獲取到了這個ViewModel,那就直接返回
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        // 沒有獲取到這個ViewModel,那就通過key去生成一個新的ViewModel
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

通過這段代碼,我們需要知道
1、key如何定義;
2、viewmodel如何創建的;

    @NonNull
    @MainThread
    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);
    }

可以看到這個方法,
1、會有一個DEFAULT_KEY+ ViewModle類的類名。
2、直接調用get(String key,Class<T> modelClass)這個方法,自定義一個key。

我們再看下如何創建這個ViewModel:

    public interface Factory {
        /**
         * Creates a new instance of the given {@code Class}.
         * <p>
         *
         * @param modelClass a {@code Class} whose instance is requested
         * @param <T>        The type parameter for the ViewModel.
         * @return a newly created ViewModel
         */
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

在get方法中可以看到這個create方法。他是在一個Factory的接口,從這個名字可以看到,這個實際上是一個工廠模式。

實現類

實現類有這么多,那到底使用哪個呢?

再回到初始化ViewModelProvider這個方法

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }

可以看到,這里會默認生成幾個Factory。源碼中包含兩個Factory的實現。NewInstanceFactory和AndroidViewModelFactory,可以簡單看下NewInstanceFactory

        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }

既然是工廠模式,那就都有不同的create方法實現,NewInstanceFactory就是直接通過class.newInstance的方法去實例化對象。

初步了解初始化之后,我們開始解決疑問:
1、這個東西為啥能在Activity和Fragment中共享數據?
初始化ViewModelProvider的方法。

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }

實際上調用的是:

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

mViewModelStore 這個東西是從Activity中傳過來的。所以在Activity中是唯一的。
前面的get方法也可以看到,一旦這個ViewModel創建出來就會被放到mViewModelStore中去保存,下一次可以直接從mViewModelStore中拿出來。那么如果Fragment需要共享Activity中的數據,只需要在初始化的時候傳入Activity和相同的ViewModel的class即可。

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        var viewModel = ViewModelProvider(requireActivity()).get(MyViewModel::class.java)
    }

這樣,拿到的mViewModelStore是Activity的,傳入的MyViewModel::class.java也會從mViewModelStore拿到已經存在的View Model,自然就可以實現數據的共享了。

2、這個東西為啥能在多個Activity中共享數據?
如果不看源碼,就可以直接理解為,這個ViewModel就是和Application的生命周期同步的,所有的Activity都能獲取到這個對象,自然也可以共享里面的數據。
1、使用AndroidViewModelFactory。如果使用AndroidViewModelFactory就可以在多個Activity中共享數據。

2、Application實現ViewModelStoreOwner,也可以做到在多個Activity中實現共享數據。

class BaseApplicaiton : Application(), ViewModelStoreOwner {

    companion object{
        var instance:BaseApplicaiton? = null
    }

    private val appViewModelStore: ViewModelStore by lazy {
        ViewModelStore()
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
    }

    override fun getViewModelStore(): ViewModelStore {
        return appViewModelStore
    }
}

3、ViewModel是如何在Activity異常生命周期恢復數據的?

保存數據可以看下ActivityThread的performDestoryActivity方法:

    ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        Class<? extends Activity> activityClass = null;
        if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
        if (r != null) {
            activityClass = r.activity.getClass();
            r.activity.mConfigChangeFlags |= configChanges;
            if (finishing) {
                r.activity.mFinished = true;
            }

            performPauseActivityIfNeeded(r, "destroy");

            if (!r.stopped) {
                callActivityOnStop(r, false /* saveState */, "destroy");
            }
            // 具體看這段代碼
            if (getNonConfigInstance) {
                try {
                    r.lastNonConfigurationInstances
                            = r.activity.retainNonConfigurationInstances();
                } catch (Exception e) {
                    if (!mInstrumentation.onException(r.activity, e)) {
                        throw new RuntimeException(
                                "Unable to retain activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
                    }
                }
            }
        // 省略若干代碼
        return r;
    }

retainNonConfigurationInstances這個方法就是將Activity的一些參數配置保存到NonConfigurationInstances 中。

    NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
        // 省略若干代碼
        return nci;
    }

onRetainNonConfigurationInstance:這里主要就是將Activity的viewModelStore保存到NonConfigurationInstances。

    public final Object onRetainNonConfigurationInstance() {
        // Maintain backward compatibility.
        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;
    }

繼續追溯源碼getLastNonConfigurationInstance方法可以看到保存數據的NonConfigurationInstances.

    /* package */ NonConfigurationInstances mLastNonConfigurationInstances;
    static final class NonConfigurationInstances {
        Object activity;
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
    }

Activity中有個ActivityClientRecord,可以理解為記錄Activity的的一些參數集合。里面就有 Activity.NonConfigurationInstances lastNonConfigurationInstances;從上面的代碼中可以看到,當Activity銷毀的時候這里就是將Activity的NonConfigurationInstances賦值給了ActivityClientRecord的NonConfigurationInstances。

如何還原數據可以看下ActivityThread的performLaunchActivity方法:

            
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);

這里會將lastNonConfigurationInstances傳入,這里調用了activity的attach方法。通過這個方法將ActivityClientRecord的NonConfigurationInstances賦值給了Activity的NonConfigurationInstances。

4、ViewModel和onSaveInstance()的區別

1、onSaveInstance是通過Bundle保存數據的,保存可序列化數據,一般大小為1M-8k。
ViewModel可以保存任何形式的數據,大小不限制,不超過系統的App分配的內存即可。
2、onSaveInstance是將數據保存的磁盤中,而ViewModel是將數據保存在內存中。

5、總結

掌握ViewModel需要了解
1、ViewModel的基本用法。
2、ViewModel的數據共享原理。
3、ViewModel是如何做到Activity異常生命周期還能保存數據的。
4、ViewModel和onSaveInstance的一些區別。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容