美文网首页
ViewModel 的重建恢复原理

ViewModel 的重建恢复原理

作者: 你可记得叫安可 | 来源:发表于2020-10-04 22:27 被阅读0次

    从 ViewModelProvider(this).get(xxViewModel.class) 讲起

    首先,我们看看 ViewModelProvider 的构造函数:

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

    ViewModelProvider 的构造函数接收一个 ViewModelStoreOwner,而 ViewModelStoreOwner 接口只有一个 getViewModelStore 方法,它返回一个 ViewModelStore。所以我们看看 ViewModelStore:

    public class ViewModelStore {
        private final HashMap<String, ViewModel> mMap = new HashMap<>();
        final void put(String key, ViewModel viewModel) {
            ViewModel oldViewModel = mMap.put(key, viewModel);
            if (oldViewModel != null) {
                oldViewModel.onCleared();
            }
        }
        final ViewModel get(String key) {
            return mMap.get(key);
        }
        Set<String> keys() {
            return new HashSet<>(mMap.keySet());
        }
        /**
         *  Clears internal storage and notifies ViewModels that they are no longer used.
         */
        public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.clear();
            }
            mMap.clear();
        }
    }
    

    实现非常简单:实现中只有一个用于存储 ViewModelhash 表:

    • key 是由 ViewModelProvider 生成的:DEFAULT_KEY + ":" + canonicalName,其中 DEFAULT_KEY = androidx.lifecycle.ViewModelProvider.DefaultKey
    • valueViewModel 的实例。

    ViewModelStoreowner 类,必须保证在发生配置改变时,发生重建后,依然返回同一个 ViewModelStore 实例。我们的 ComponentActivity 就是一个 ViewModelStoreOwner,我们在后面小节将会看看在重建后,它是如何返回同一个 ViewModelStore 实例的。

    ViewModelProvider.Factory

    ViewModelProvider 的带两个参数的构造函数的第二个参数是一个 Factory

    /**
     * Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
     */
    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);
    }
    

    从注释和接口代码中我们可以看出,它是一个实例化 ViewModel 的工厂,只有一个产生 ViewModel 实例的 create 方法。从方法签名中我们就可以看出,它接收 Class,通过反射之类的方法来构造 ViewModel。通常我们默认使用的是 ViewModelProvider.NewInstanceFactory

    /**
     * Simple factory, which calls empty constructor on the give class.
     */
    public static class NewInstanceFactory implements Factory {
        private static NewInstanceFactory sInstance;
        /**
         * Retrieve a singleton instance of NewInstanceFactory.
         *
         * @return A valid {@link NewInstanceFactory}
         */
        @NonNull
        static NewInstanceFactory getInstance() {
            if (sInstance == null) {
                sInstance = new NewInstanceFactory();
            }
            return sInstance;
        }
        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @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);
            }
        }
    }
    

    可以看到,默认的 NewInstanceFactory 只是简单地反射调用 ViewModel 的无参构造函数来实例化传入的 ViewModel

    Activity 中 ViewModel 的创建和获取

    我们从常用的获取 ViewModel 方法出发:

    ChronometerViewModel chronometerViewModel
            = new ViewModelProvider(this).get(ChronometerViewModel.class);
    👇
    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");
        }
        // 这里的 get 方法就生成了我们上面 ViewModelStore 中的 key 值了
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    👇
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        // 从 ViewModelStore 中获取 ViewModel 实例
        ViewModel viewModel = mViewModelStore.get(key);
        // 如果获取到,且确实是我们想要获取的 ViewModel 类型,则直接返回
        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.
            }
        }
        // 如果没有获取到 ViewModel 实例,则调用 Factory 进行创建
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        // 将创建出来的 ViewModel 存入 ViewModelStore
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
    

    Activity 中 ViewModel 的保存和恢复

    ViewModel 的保存和恢复是利用了 Activity.onRetainNonConfigurationInstance 机制。我们来看支持 ViewModelComponentActivity.onRetainNonConfigurationInstance

    @Override
    @Nullable
    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;
        }
        // 将 ViewModelStore 保存在 NonConfigurationInstances 中。保存了 ViewModelStore 也就保存了 ViewModel
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }
    

    从上面我们可以看到,ViewModel 的保存是依赖于 onRetainNonConfigurationInstance 的。ViewModel 的恢复我们来看看 ViewModelProvider 的构造方法:

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

    上面传入 Activity 作为 ViewModelStoreOwner,当调用它的 getViewModelStoreViewModelStore 也就被恢复了:

    @NonNull
    @Override
    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) {
            // 这里获得的 NonConfigurationInstance 就是之前通过 onRetainNonConfigurationInstance 保存的
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
    

    我们通过方法 getLastNonConfigurationInstance 获取到之前保存的 NonConfigurationInstances,从而取出其中的 ViewModelStore
    通过上面的分析,我们知道,保存恢复 ViewModel 的关键在于 onRetainNonConfigurationInstancegetLastNonConfigurationInstance 机制是如何起作用的?
    通过查找方法引用,我们可以看到 onRetainNonConfigurationInstance 方法其实是在 Activity.retainNonConfigurationInstances 方法中被调用的:

    NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
        // We're already stopped but we've been asked to retain.
        // Our fragments are taken care of but we need to mark the loaders for retention.
        // In order to do this correctly we need to restart the loaders first before
        // handing them off to the next activity.
        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
        if (activity == null && children == null && fragments == null && loaders == null
                && mVoiceInteractor == null) {
            return null;
        }
        // 注意 Activity 中的 NonConfigurationInstances 类与 ComponentActivity 中的 NonConfigurationInstances 类不一样
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;  // 这里将 onRetainNonConfigurationInstance 返回值保存了下来,其中就包含了 ViewModelStore
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = loaders;
        if (mVoiceInteractor != null) {
            mVoiceInteractor.retainInstance();
            nci.voiceInteractor = mVoiceInteractor;
        }
        return nci;
    }
    

    Activity.retainNonConfigurationInstances 方法是在 ActivityThread.performDestroyActivity 方法中被调用的。而 ComponentActivity 中的 mLastNonConfigurationInstances 则是在 ComponentActivity.attach 方法中,由 ActivityThread.performLaunchActivity 时主动调用传入进来的。
    因此综上所述,ViewModelStoreonRetainNonConfigurationInstance 时被保存在了 NonConfigurationInstances 实例中,该实例被 ActivityThread 持有。下次 Activity 重建时,由 ActivityThread.performLaunchActivity 方法中调用 Activity.attach 方法,再讲 NonConfigurationInstances 实例传给重建后的 Activity

    Activity 中 ViewModel 的销毁

    通过上节我们知道 ViewModel 可以跨越 Activity 的生命周期和重建,那么 ActivityViewModel 怎么知道需要在什么时候销毁呢?

    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();
                }
            }
        }
    });
    

    如上面代码所示,ViewModel 正是借助了生命周期的回调,在 ON_DESTROY 回调时,通过 isChangingConfigurations 方法判断该 Activity 是否正处于因配置改变而重建中,如果不是的话,则可以清楚 ViewModel

    Fragment 中 ViewModel 的创建和获取

    由于 Fragment 同样也是实现了 ViewModelStoreOwner 接口,因此同在 Activity 中创建和获取 ViewModel 一样,给 ViewModelProvider 的构造函数传入自己的实例即可:

    mSeekBarViewModel = new ViewModelProvider(this).get(SeekBarViewModel.class);
    

    如果 Fragment 要和其宿主 Activity 共用一个 ViewModel,则可以向 ViewModelProvider 的构造函数传入其宿主 Activity 即可:

    mSeekBarViewModel = new ViewModelProvider(requireActivity()).get(SeekBarViewModel.class);
    

    但是与 Activity 中创建 ViewModel 不同,因为创建和获取 ViewModel 在于 getViewModelStore 方法,而这个方法的实现上,ActivityFragment 是不相同的。

    • Fragment.getViewModelStore 是从 FragmentManager.getViewModelStore 中获取的。
    • FragmentActivity.getViewModelStore 是从 ComponentActivity.getViewModelStore 获取的,它是直接存储在 mViewModelStore 中的。

    因此我们下面分析的重点应该在于 FragmentManager.getViewModelStore 的机制。

    FragmentActivity 在初始化时做了什么

    我们可以注意到在 FragmentActivity.onCreate 中调用了 mFragments.attachHost

    mFragments.attachHost(null /*parent*/);
    

    最终调用到了 FragmentManager.attachController (注意,这里的 FragmentManager 就是根 Fragment 中的 mFragmentManager):

    // 该方法在 FragmentManager 中。host 为 FragmentActivity.HostCallbacks。
    void attachController(@NonNull FragmentHostCallback<?> host,
            @NonNull FragmentContainer container, @Nullable final Fragment parent) {
        ...
        // Get the FragmentManagerViewModel
        if (parent != null) {
            mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
        } else if (host instanceof ViewModelStoreOwner) { // 👈调用到这里
            // 这里最终获取的是 FragmentActivity 的 ViewModelStore
            ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
            // mNonConfig 这个 FragmentManager 对应的 Fragment 对应的 ViewModel。
            mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
        } else {
            mNonConfig = new FragmentManagerViewModel(false);
        }
    }
    

    因为在 FragmentActivity 中传入的 parentnull,且 FragmentActivity.HostCallbacks 实现了 ViewModelStoreOwner,因此会走到上面的第二个条件分支。而 FragmentActivity.HostCallbacks.getViewModelStore 就是调用的 FragmentActivity.getViewModelSotre因此上面代码创建的 FragmentManagerViewModel 的实例 mNonConfig,其实存入到了 FragmentActivityViewModelStore 中,而且还换存在了 FragmentManager.mNonConfig(注意 mNonConfig 是一个 FragmentManagerViewModel 的实例) 中,这两点很重要。

    在 Fragment 中获取 ViewModel

    我们知道关键点在于调用的 Fragment.getViewModelStore

    public ViewModelStore getViewModelStore() {
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        // 这里的 mFragmentManager 实例就是 FragmentActivity.HostCallbacks 内部类中的 mFragmentManager,
        // 也就是在上一节中我们提到拥有 FragmentManagerViewModel 的那个 FragmentManager
        return mFragmentManager.getViewModelStore(this);
    }
    
    // FragmentManager 👇
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        // 这里的 mNonConfig 根据上节,是一个 FragmentManagerViewModel 的实例
        return mNonConfig.getViewModelStore(f);
    }
    

    从上面代码和注释中我们可以知道

    • FragmentgetViewModelStore 最终是从 FragmentManagerViewModel.getViewModelStore 中获取的。这与 FragmentActivity: ComponentActivitygetViewModelStore 来源不一样,因此在 FragmentActivityFragment 中都获取同样类型(类名一样)的 ViewModel,获取出来的实例却是不一样的。
    • 根据上一节中我们可以知道,根 Fragment.getViewModelStore 的最终调用处 FragmentManagerViewModel.getViewModelStore 中的 FragmentManagerViewModel 实例,其实在 FragmentActivity.onCreate 时就已经存入了 FragmentActivityViewModelStore 中。也就是说它们之间的结构是下面这样的:
      FragmentManagerViewModel 与 Activity 的关系
    Fragment 创建时做了什么

    正如 FragmentActivityonCreate 时就已经把,用于存储 根Fragment 对应的 ViewModelStoreFragmentManagerViewModel,缓存在了 FragmentActivity 自己的 ViewModelStore 中,那么 根Fragment 是否也对 子Fragment 做了同样的准备?

    Fragment 中有一个成员变量 mChildFragmentManager,它是管理 子FragmentFragmentManager。因此我们可以注意到,根 Fragment 对应的 FragmentManagerFragmentActivity.FragmentController.mHost.mFragmentManager,而 子Fragment 对应的 FragmentManagerFragment.mChildFragmentManager
    Fragment 中还有一个成员变量 mFragmentManager,它就是管理 本FragmentFragmentManager,它的实例其实是来自 父Fragment.getChildFragmentManager

    根Fragment 关联到 Activity 上时,会调用其 performAttach 方法:

    void performAttach() {
        // 类比于 FragmentActivity.onCreate 中的 mFragments.attachHost -> mHost.mFragmentManager.attachController
        mChildFragmentManager.attachController(mHost, new FragmentContainer() {
            @Override
            @Nullable
            public View onFindViewById(int id) {
                if (mView == null) {
                    throw new IllegalStateException("Fragment " + this + " does not have a view");
                }
                return mView.findViewById(id);
            }
            @Override
            public boolean onHasView() {
                return (mView != null);
            }
        }, this);
        mState = ATTACHED;
        mCalled = false;
        onAttach(mHost.getContext());
        if (!mCalled) {
            throw new SuperNotCalledException("Fragment " + this
                    + " did not call through to super.onAttach()");
        }
    }
    

    正如 FragmentActivity 中最终调用到了 HostCallbacks.mFragmentManager.attachControllerFragment.performAttach 最终也调用到了 mChildFragmentManager.attachController

    void attachController(@NonNull FragmentHostCallback<?> host,
            @NonNull FragmentContainer container, @Nullable final Fragment parent) {
        ...
        // Get the FragmentManagerViewModel
        if (parent != null) {
            此时 parent != null,走这里 👇。下面的 parent 是指的根 Fragment,所以下面的 mFragmentManager 是 FragmentActivity 的 FragmentManager
            mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
        } else if (host instanceof ViewModelStoreOwner) {
            ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
            mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
        } else {
            mNonConfig = new FragmentManagerViewModel(false);
        }
    }
    

    最终调用到了 FragmentActivityFragmentManagerViewModel.getChildNonConfig(parent)

    // 参数 f 是 根Fragment
    FragmentManagerViewModel getChildNonConfig(@NonNull Fragment f) {
        FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
        if (childNonConfig == null) {
            childNonConfig = new FragmentManagerViewModel(mStateAutomaticallySaved);
            mChildNonConfigs.put(f.mWho, childNonConfig);
        }
        return childNonConfig;
    }
    

    如上所示,FragmentManagerViewModel 中还有一个数据结构 mChildNonConfigs,它是一个 keyFragmentManagerViewModel 所对应 子FragmentIDvalue子FragmentFragmentManagerViewModelHashMap
    上面代码会根据本 Fragment ID 去找 子FragmentFragmentManagerViewModel 实例,如果有则存到 mChildNonConfigs 中,如果没有则构造一个新的实例存入到 mChildNonConfigs 中。
    因此,如果一个 Fragment 下面有多个 子Fragment,那么这些 子Fragment 所对应的 FragmentManagerViewModel 都是同一个,且存在 父Fragment.mChildNonConfigs 中。在上例中就是 FragmentActivity 下面有多个 根Fragment,那么这些 根Fragment 所对应的 FragmentManagerViewModel 都是同一个,且都存在 FragmentActivityFragmentManager.mChildNonConfigs 中。

    所以 子FragmentViewModel 能够恢复的原因,与 根FragmentViewModel 能够恢复的原因是一样的:在 根Fragment 最初与 FragmentActivity 绑定时,根Fragment 就通过 performAttach,在 根Fragment 对应的 FragmentManagerViewModel 中,以 根Fragment IDkey,新构造的 FragmentManagerViewModelvalue,初始化了 根FragmentFragmentManagerViewModel.mChildNonConfigs。之后 子FragmentFragmentManagerViewModel 就会放在 mChildNonConfigs 中。

    FragmentManagerViewModel 之间的关系
    番外:理解 FragmentHostCallback 和 FragmentController

    FragmentHostCallback 的类注释中我们可以知道,该类主要是为了面向 Fragment 宿主提供一个统一的 Fragment 相关信息的回调,包括 onGetLayoutInflateronShouldSaveFragmentState 等。这些回调有的来自 Fragment 类,有的来自 FragmentManager 类,因此 FragmentHostCallbackFragment 向外暴露信息的回调的整合点。目前 FragmentHostCallback 的唯一实现类是 FragmentActivity 的内部类 HostCallbacks。内部类可以直接调用它的外部类,这样,Fragment 的相关信息回调就通过 FragmentActivity.HostCallbacks 传给了 FragmentActivity

    FragmentHostCallback 类不仅向宿主类提供 Fragment 的相关事件回调,它还持有一个 FragmentManager 的实例 mFragmentManager,并且该实例在 FragmentHostCallback 中没有任何使用。我们查看 mFragmentManager 使用的地方可以发现都是在 FragmentController 中。

    FragmentController 通过构造函数传入 FragmentHostCallback 的实例 mHost,再通过 mHost 调用到 mFragmentManager。我们看看 FragmentController 的方法,可以发现几乎都是宿主 FragmentActivity 主动调用的 生命周期方法 以及 需要从 Fragment 中获取数据的方法,例如获取 FragmentManager 实例。

    FragmentController.png

    相关文章

      网友评论

          本文标题:ViewModel 的重建恢复原理

          本文链接:https://www.haomeiwen.com/subject/fzpxuktx.html