Jetpack第四篇:ViewModel

570 阅读5分钟

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 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的接口,从这个名字可以看到,这个实际上是一个工厂模式。

1930161-cbc4b7ad51580720.webp

实现类有这么多,那到底使用哪个呢?

再回到初始化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的一些区别。