ViewModel 基本原理

Activity 配置发生改变,如屏幕旋转时,会销毁原本的 Activity 再创建一个新的实例,会走一次 Activity 的生命周期。传统的应对方式是在 Activity 的 onCreate()/onSaveInstanceState() 中通过获取/保存 savedInstanceState 参数实现状态保存。但是在使用 ViewModel 后,就无须我们手动处理了。

ViewModel 对状态保存的实现原理,主要用到了 onRetainCustomNonConfigurationInstance() 和 getLastCustomNonConfigurationInstance() 两个方法,官方不建议我们直接使用这两个方法,因此废弃了这两个方法,而是通过 ViewModel 来保存非配置数据:

// ComponentActivity.java
	@Deprecated
    @Nullable
    public Object onRetainCustomNonConfigurationInstance() {
        return null;
    }

    @Deprecated
    @Nullable
    public Object getLastCustomNonConfigurationInstance() {
        NonConfigurationInstances nc = (NonConfigurationInstances)
                getLastNonConfigurationInstance();
        return nc != null ? nc.custom : null;
    }

那么如何使用 ViewModel 来保存非配置数据呢?我们从创建 ViewModel 对象开始看起:

	XxxViewModel xxxViewModel = new ViewModelProvider(this).get(XxxViewModel.class);

1、ViewModelProvider 初始化

先看 ViewModelProvider 的构造方法:

	// 创建 ViewModel 的工厂
	private final Factory mFactory;
	// 保存 ViewModel 的仓库
    private final ViewModelStore mViewModelStore;
    
	/**
	* ViewModelStoreOwner 决定了 mFactory 的初始化,由于 ComponentActivity 是 ViewModelStoreOwner 的实
	* 现类,同时也实现了 HasDefaultViewModelProviderFactory 接口(在 activity 1.1 版本才实现),由于我们
	* 创建的 Activity 都是 AppCompatActivity 的子类,应该取冒号前面的值,getDefaultViewModelProviderFactory()
	* 会返回一个 SavedStateViewModelFactory
	*/
	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;
    }

这个 ViewModelStoreOwner 是一个接口,ComponentActivity 实现了它:

	@NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        ...
        // 第一次调用时 mViewModelStore 为空
        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;
    }

ComponentActivity 有两种方式获取 ViewModelStore 实例,首先通过 NonConfigurationInstances 上保存的 viewModelStore 字段,如果失败才调用 ViewModelStore 的构造方法获取。

ViewModelStore 内部通过一个 HashMap 保存 ViewModel:

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    
    // put() get() keys() clear() 都是常规操作,省略……
}

getLastNonConfigurationInstance() 的实现在 Activity 中,会返回 mLastNonConfigurationInstances 内保存的 Activity 的非配置数据:

	@Nullable
    public Object getLastNonConfigurationInstance() {
        // mLastNonConfigurationInstances.activity 不是一个 Activity 对象,
        // 而是一个保存了 Activity 内非配置数据的对象
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

这样我们大致能看出 ViewModelProvider 的结构:

  • ViewModelStore:持有一个 HashMap 缓存所有的 ViewModel
  • Factory:默认是一个 SavedStateViewModelFactory,当通过 ViewModelProvider 获取 ViewModel 对象时,先从 ViewModelStore 的缓存中取,如果未命中才通过工厂创建 ViewModel 对象

2、获取 ViewModel 对象

上一步创建了 ViewModelProvider 对象,通过 ViewModelProvider.get() 可以获取一个 ViewModel 对象。获取时,先去 mViewModelStore 这个缓存中找,如果未命中才通过 ViewModelProvider.Factory 这个工厂创建 ViewModel 并缓存:

	@NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        // 匿名类拿到的 canonicalName 为空,不能创建 ViewModel
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }

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

        // 1.如果 viewModel 的 Class 对象是由 modelClass 派生出来的就直接返回 viewModel
        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            if (viewModel != null) {
            }
        }
        // 2.通过 mFactory 以反射方式创建 viewModel
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        // 3.缓存新创建出来的 viewModel 对象
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

对于 AppCompatActivity 的子类来说,mFactory 在默认情况下是一个 SavedStateViewModelFactory,它是 KeyedFactory 的子类,所以会走 if 条件创建 viewModel:

	@NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor<T> constructor;
        // 1.寻找 modelClass 的构造方法
        if (isAndroidViewModel) {
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // 2.构造方法都会用到 SavedStateHandle 作为参数,如果构造方法为空,说明用不到
        // SavedStateHandle,直接通过工厂创建 viewmodel 对象并返回
        if (constructor == null) {
            return mFactory.create(modelClass);
        }

        // 3.走到这说明构造方法不为空,那么就先获得 SavedStateHandle 的提供者
        // SavedStateHandleController,然后通过构造方法生成 viewmodel
        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 ...
    }

上述过程中,第 1 步实际上是根据参数类型列表找到对应的构造方法:

	private static final Class<?>[] ANDROID_VIEWMODEL_SIGNATURE = new Class[]{Application.class,
            SavedStateHandle.class};
    private static final Class<?>[] VIEWMODEL_SIGNATURE = new Class[]{SavedStateHandle.class};

	// 从 modelClass 类中根据参数类型找到匹配的构造方法
    @SuppressWarnings("unchecked")
    private static <T> Constructor<T> findMatchingConstructor(Class<T> modelClass,
            Class<?>[] signature) {
        for (Constructor<?> constructor : modelClass.getConstructors()) {
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            if (Arrays.equals(signature, parameterTypes)) {
                return (Constructor<T>) constructor;
            }
        }
        return null;
    }

如果找到了就对该构造方法执行 newInstance() 创建出 ViewModel 对象,否则通过工厂的 create() 创建:

	public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
	
		@NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } catch ...
            }
            // 如果 modelClass 不是派生自 AndroidViewModel,则使用父类的 create() 创建一个空参对象
            return super.create(modelClass);
        }
	}

	public static class NewInstanceFactory implements Factory {
    	@NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.newInstance();
            } catch ...
        }
    }

3、状态保存

系统配置发生改变时(如屏幕旋转),系统会先销毁原来的 Activity 并创建一个新的 Activity 实例。在该过程中,AMS 会先调用 ActivityThread 的 handleRelaunchActivity():

	@Override
    public void handleRelaunchActivity(ActivityClientRecord tmp,
            PendingTransactionActions pendingActions) {
        ...
        // 获取要重新启动的 ActivityClientRecord
        ActivityClientRecord r = mActivities.get(tmp.token);
        if (r == null) {
            return;
        }
        ...
        // 开启调用流程
        handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
                pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
        ...
    }

	private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
            PendingTransactionActions pendingActions, boolean startsNotResumed,
            Configuration overrideConfig, String reason) {
        // Preserve last used intent, it may be set from Activity#setIntent().
        final Intent customIntent = r.activity.mIntent;
        // Need to ensure state is saved.
        if (!r.paused) {
            performPauseActivity(r, false, reason, null /* pendingActions */);
        }
        if (!r.stopped) {
            callActivityOnStop(r, true /* saveState */, reason);
        }

        // 处理 Activity 的销毁
        handleDestroyActivity(r, false, configChanges, true, reason);

        r.activity = null;
        r.window = null;
        r.hideForNow = false;
        r.nextIdle = null;
        ...
        // Activity 重新启动
        handleLaunchActivity(r, pendingActions, customIntent);
    }

ActivityThread 会先销毁当前的 Activity,在执行销毁过程中,会让 Activity 调用 retainNonConfigurationInstances() 将所有需要恢复的数据,我们称之为非配置实例,保存到 ActivityClientRecord 中:

	@Override
    public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges,
            boolean getNonConfigInstance, String reason) {
        performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason);
        ...
    }

	// getNonConfigInstance 表示是否要保存非配置实例
	void performDestroyActivity(ActivityClientRecord r, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
    	...
    	if (getNonConfigInstance) {
            try {
                r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
            } catch (Exception e) {
                ...
            }
        }
        ...
    }

Activity 这边会调用 retainNonConfigurationInstances() 得到一个 NonConfigurationInstances 对象:

	NonConfigurationInstances retainNonConfigurationInstances() {
        // 1.1 获取 Activity 的非配置数据 —— ComponentActivity.NonConfigurationInstances 
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        // 1.2 获取 Fragment 的非配置数据
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        ...

        // 2.创建 NonConfigurationInstances,注意这个是 Activity 的内部类,包含 5 个
        // 成员,而 onRetainNonConfigurationInstance() 中的 NonConfigurationInstances 是
        // ComponentActivity 的内部类,只定义了 custom 和 viewModelStore 两个成员
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = loaders;
        ...
        return nci;
    }

首先调用 onRetainNonConfigurationInstance(),该方法用来保留所有适当的非配置实例,只不过 Activity 返回空没有给出实现,而其子类 ComponentActivity 实现了该方法:

	/**
	* 保留所有适当的【非配置实例】,不要自己重写这个方法,如果你想保存你自己的
	* 【非配置实例】,请使用 ViewModel
	*/
	@Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        // 1.获取自定义的非配置实例。默认是空,需要重写
        Object custom = onRetainCustomNonConfigurationInstance();

        // 2.获取 viewModelStore,如果 mViewModelStore 为空则尝试从 
        // NonConfigurationInstances 中获取 viewModelStore
        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;
        }

        // 3.创建一个新的 NonConfigurationInstances 保存 custom 和 viewModelStore 并返回
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }

可见 Activity 在保存非配置实例时,在 onRetainNonConfigurationInstance() 内会将用户自定义的保存非配置实例的对象 custom 和当前 Activity 的 ViewModelStore 封装进 ComponentActivity.NonConfigurationInstances 作为返回结果。

然后这个返回结果又作为 Activity 的非配置实例被封装进 Activity.NonConfigurationInstances 的 activity 字段中,作为 retainNonConfigurationInstances() 的返回值被保存到 ActivityClientRecord.lastNonConfigurationInstances 字段中。因此只要不是应用进程被彻底杀掉,这个记录就会一直保存。

当然,如果不是因为配置发生变化而导致的 Activity 销毁(如正常退出 Activity)时,会主动清空该 Activity 的 ViewModelStore:

	public ComponentActivity() {
		...
		getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    // 销毁且没有改变系统配置时,清空 ViewModelStore
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
        ...
	}

4、状态恢复

在 ActivityThread 的 handleRelaunchActivityInner() 中,先是调用 handleDestroyActivity() 处理 Activity 的销毁过程,保存 NonConfigurationInstances 到 ActivityClientRecord 中,在最后又会调用 handleLaunchActivity() 启动新的 Activity:

	@Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
    	...
    	final Activity a = performLaunchActivity(r, customIntent);
    	...
    }
    
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    	...
    	// 创建 Activity 对象
    	Activity activity = null;
    	try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
                    appContext.getAttributionSource());
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch ...
        ...
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            ...
            if (activity != null) {
            	...
            	// 执行 Activity 的 attach(),将 lastNonConfigurationInstances 传过去
            	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, r.shareableActivityToken);
                ...
            }
            ...
        } catch ...
        
        return activity;
    }

启动 Activity 时,会先创建一个新的 Activity 对象,创建完成后会调用 Activity 的 attach() 将原本保存在 ActivityClientRecord 的 lastNonConfigurationInstances 传过去保存在 Activity 的成员变量中:

	NonConfigurationInstances mLastNonConfigurationInstances;
	
	final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
            IBinder shareableActivityToken) {
    	...
    	mLastNonConfigurationInstances = lastNonConfigurationInstances;
    	...
    }

这个 mLastNonConfigurationInstances 可以通过 getLastNonConfigurationInstance() 获取:

	@Nullable
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值