ViewModel 的重建恢复原理

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

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

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

上面代码我们有两个地方需要搞清楚,ViewModelStoreViewModelProvider.NewInstanceFactory。大体上我们可以猜测到,前者是存储 ViewModel 实例的,后者是构造 ViewModel 实例的工厂。

1.1 ViewModelStore(存储 ViewModel 的实例)

ViewModelProvider 的构造函数接收一个 ViewModelStoreOwner,而 ViewModelStoreOwner 接口只有一个 getViewModelStore 方法,它返回一个 ViewModelStore。所以我们看看 ViewModelStore(顾名思义可以猜到它就是用来存储 ViewModel 实例的):

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 的实例。

有点类似 ThreadLocal 的实现原理,本质上是有一个 Entry,它存储了 ThreadLocalvalue 的映射

因此,ViewModelStore 的职责是保存 ViewModel 的实例,而 ViewModelStoreowner 类,则必须保证在发生配置改变时,发生重建后,依然返回同一个 ViewModelStore 实例(这样,我们就可以返回同一个 ViewModel)。我们的 ComponentActivity 就是一个 ViewModelStoreOwner,我们在后面小节将会看看在重建后,它是如何返回同一个 ViewModelStore 实例的。

1.2 ViewModelProvider.Factory(构造 ViewModel 实例的工厂方法)

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

2. 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;
}

从上面可以看到,当我们调用 new ViewModelProvider(this).get(xxViewModel.class); 来实例化并获取 xxViewModel 实例时,其实就是从 1.1ViewModelStore 中去取,如果没有就用 1.2newInstanceFactory 新构造一个,并放入 1.1ViewModelStore 以方便以后获取同一个实例。

所以,配置重建后,能够获取同一个 ViewModel 实例的关键在于能够获取到同一个 ViewModelStore 的实例。

3. Activity 中 ViewModel 的保存和恢复

上一节中我们说了,ViewModel 的保存和恢复是本质上依赖于 ViewModelStore 的保存和恢复。而 ViewModelStore 的保存和恢复则是利用了 Activity.onRetainNonConfigurationInstance 机制。

3.1 ViewModelStore 的保存

我们来看支持 ViewModel 机制的 ComponentActivity.onRetainNonConfigurationInstance 中是如何保存 ViewModelStore 的:

@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;
}

从上面我们可以看到,ViewModelStore保存是依赖于 onRetainNonConfigurationInstance 的,在 onRetainNonConfigurationInstance 回调时,构建了新的 NonConfigurationInstance 实例,并将现在的 ViewModelStore 赋值给它的 nci.viewModelStore 属性。

3.2 ViewModelStore 的恢复

ViewModelStoreViewModel 的恢复其实就是当我们使用 ViewModelProvider 来获取 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

4. onRetainNonConfigurationInstance 机制

通过上面的分析,我们知道,保存恢复 ViewModelStoreViewModel 的关键在于 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 时主动调用传入进来的。

因此综上所述,ViewModelStoreActivity.onDestroy() 销毁时,通过 onRetainNonConfigurationInstance 时的回调被保存在了 NonConfigurationInstances 实例中,该实例被 ActivityThread 持有。下次 Activity 重建时,由 ActivityThread.performLaunchActivity 方法中调用 Activity.attach 方法,再将 NonConfigurationInstances 实例传给重建后的 Activity

5. 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

6. 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);

由上面的 (2) 我们知道,创建和获取 ViewModel 的关键在于 getViewModelStore 方法,而这个方法的实现上,ActivityFragment 是不相同的:

  • FragmentgetViewModelStore 是从 FragmentManager.getViewModelStore 中获取的。
  • ActivitygetViewModelStore 是从 ComponentActivity.getViewModelStore 获取的,它是直接存储在 mViewModelStore 中的(根据 (4)mViewModelStore 是在 Activity attach 时,由 ActivityThread 传入的)。

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

6.1 FragmentActivity 在初始化时做了什么

Fragment 是依附于 FragmentActivity 的,因此对 FragmentManager 的操作一定也是通过 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) { // FragmentActivity 中传入的 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(是的,HostCallbacks 就是绕了一下而已),因此上面代码创建的实例 mNonConfig: FragmentManagerViewModel,其实存入到了两个地方:
1. FragmentActivityViewModelStore 中,所以 FragmentActivity.mViewModelStore 中不仅有 Activity 环境下创建的 ViewModel ,还包含了一个(或多个)根 Fragment 对应的 FragmentManagerViewModel
2. FragmentManager.mNonConfig(注意 mNonConfig 是一个 FragmentManagerViewModel 的实例) 中

这两点很重要。

6.2 在根 Fragment 中获取 ViewModel

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

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

// FragmentManager 👇
ViewModelStore getViewModelStore(@NonNull Fragment f) {
    // 这里的 mNonConfig 根据 (6.1) 节,是一个 FragmentManagerViewModel 的实例
    return mNonConfig.getViewModelStore(f);
}

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

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

所以,Activity 中创建的 ViewModel 是来自于它的 mViewModelStore。而根 Fragment 中创建的 ViewModel 是来自于它的宿主 ActivitymViewModelStore.FragmentManagerViewModel.mViewModelStores

这里 mViewModelStores 是复数的原因是,一个 Activity 可能有多个根 Fragment

6.3 Fragment 创建时做了什么

(6.2) 最后的结论我们知道,FragmentActivityonCreate 时就做好了如下引用链:activity: FragmentActivitymViewModelStore: ViewModelStore → 里面有一个 key 对应的是 FragmentManagerViewModelFragmentManagerViewModel.mViewModelStores

Q: 那么 根Fragment 是否也对 子Fragment 做了同样的准备?
各个层级的 Fragment 与它对应的 FragmentManager 有如下对应关系:

  • 根FragmentFragmentActivity.FragmentController.mHost.mFragmentManager 以及它自己的 mFragmentManager: FragmentManagerImpl
  • 子Fragment父Fragment.getChildFragmentManager 以及 它自己的 mFragmentManager: FragmentManagerImpl

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

// androidx.fragment.app.Fragment
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()");
    }
}

之后,它们通过各自的 FragmentManager 操作,在各层级的 FragmentManagerViewModel 之间建立了如下图的关系。

FragmentManagerViewModel 之间的关系

所以,各层级的 FragmentViewModel 能够恢复,也是受益于 ActivityViewModelStore 的恢复。

番外:理解 FragmentHostCallback 和 FragmentController

从上面的代码中,我们可以注意到在 FragmentActivity 中有一个内部类 HostCallbacks,这个类继承自 FragmentHostCallback,但是它的所有实现方法都只是调用它的外部类 FragmentActivity 的相应方法而已,它自己没有任何实质的操作。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。