ViewModel 是数据与 UI 分离的中间层,提供了一个将数据转换为 UI 友好型数据的场所。其次,它也提供了多 Fragment 复用相同 ViewModel 的机制。
简单使用
class UserViewModel(): ViewModel() {
val userLiveData = LiveData<User>()
override fun onCleared(){
// clear 工作,例如 Rxjava 里取消订阅
}
}
class UserFragment(): Fragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val viewModel = ViewModelProviders.of(this).get(UserViewModel::class.java)
viewModel.userLiveData.observe(viewLifecycleOwner, Observer {
// update ui
})
}
}
如果你想在多个 Fragment (同一个 Activity )里复用这个 UserViewModel , 那么只需要将 ViewModelProviders.of(this) 更换为 ViewModelProviders.of(activity) 即可, 这样 UserViewModel 的生存时间就会和 activity 一致。
注意:这里我使用了 viewLifecycleOwner 而没用使用 lifecycleOwner 。 而 viewLifecycleOwner 是在 androidx 中才存在,这主要是解决 Fragment 中 View 的生命周期与 Fragment 的生命周期不同步的问题。 假设从 FragmentA 跳转到 FragmentB 时, FragmentA 会经历下列生命周期:
onPause -> onStop -> onDestroyView
从 FragmentB 返回到 FragmentA 时, FragmentA 又会重新经历下列生命周期:
onCreateView -> onActivityCreated -> onStart -> onResume
如果用 Fragment 的生命周期 lifecycleOwner , 那么将会在FragmentB 返回到 FragmentA 时产生新的 observe 订阅,而旧的订阅并没有被销毁。在之前的版本,可能需要开发者自己去处理重复订阅的问题,而新版 androidx 则提供了 View 的生命周期 viewLifecycleOwner , 开发者可以视需求而采用不同的生命周期。
源码解析
ViewModel 并不是由用户直接通过构造器生成的,而是通过 ViewModelProvider 来获取,这使得使用者不用关心 ViewModel 的生命周期,全部交给框架内部解决。 框架提供了 ViewModelProviders 这个工具方法来提供各种场景下的 ViewModelProvider
// 提供一个将 viewModel 存储 在 Fragment 的 ViewModelProvider
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(fragment.getViewModelStore(), factory);
}
// 提供一个将 viewModel 存储 在 Activity 的 ViewModelProvider
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
我们可以看出,其关键根据传参是 Fragment 还是 FragmentActivity 而提供不同的 ViewModelStore :
- 如果是 Fragment , 则 ViewModelStore 依附在 Fragment 上,它提供的 ViewModel 可以存活至 Fragment 销毁。
- 如果是 FragmentActivity ,则 ViewModelStore 依附在 FragmentActivity 上,它提供的 ViewModel 可以存活至 FragmentActivity 销毁。 因此这些 ViewModel 可以在多个 Fragment 中共享。
另外值得一提的是:只有 androidx 中的 Fragment 和 FragmentActivity 才实现了 ViewModelStoreOwner 接口。 所以 androidx 和这之前 support-v4 里的实现并不一样。 旧版本的实现也值得一提,这个我稍后再详细介绍。
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
// Fragment 关于 ViewModelStoreOwner 的实现
public class Fragment implements ViewModelStoreOwner, ... {
// 省略其它源码...
// ViewModelStore for storing ViewModels associated with this Fragment
ViewModelStore mViewModelStore;
public ViewModelStore getViewModelStore() {
if (getContext() == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
public void onDestroy() {
mCalled = true;
FragmentActivity activity = getActivity();
boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
if (mViewModelStore != null && !isChangingConfigurations) {
mViewModelStore.clear();
}
}
}
// FragmentActivity 关于 ViewModelStoreOwner 的实现
public class FragmentActivity extends ComponentActivity implements ViewModelStoreOwner, ... {
private ViewModelStore mViewModelStore;
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) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
protected void onCreate(@Nullable Bundle savedInstanceState) {
//...
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
mViewModelStore = nc.viewModelStore;
}
// ...
}
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
}
而 ViewModelStore 本身的实现比较容易,基本就是一个 HashMap 来保存 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);
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
ViewModelStore 的问题解决了,我们就可以看看 ViewModelProvider 是如何构造 ViewModel 实例并存入 ViewModelFactory 的了。首先看看 ViewModelProvider 里提供的 Factory 接口:
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);
}
通过名字我们就可以看出,这是一个工厂模式的应用,我们可以通过实例出不同的 Factory 来以不同的方式构造 ViewModel ,当然,如果我们没有特殊需求,可以直接使用框架提供的默认 Factory 来构造无参数的 ViewModel 。 如果我们需要传参,则我们需要自己实现 Factory 接口了。
public class ViewModelProvider {
private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey";
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
//...
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
// 如果已经存在,则判断类型是否匹配
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
// 如果不存在,则通过 Factory 创建,并放入 mViewModelStore 中缓存
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
}
至此,整个 ViewModel 就说得差不多了,使用简单,原理也不是很难,但是功能确实强大。接下来会详细介绍下 ViewModel 1.x 版本时 ViewModelStore 的获取。那个时候, Fragment 和 FragmentActivity 还没有实现 ViewModelStoreOwner 。那我们又如何无感知的让生命周期融入框架中呢?
其实这里用了一个技巧,一个添加到 FragmentManager 的 Fragment 生命周期与 Activity 或者 parent fragment 的生命周期是一致的, 因此可以通过向 Activity 或者 Fragment 添加一个无 View 的 Fragment 来获取其生命周期,再将 ViewModelStore 依附在 这个 Fragment 上。 如果有看过 glide 源码的话,就可以发现其实它也是这么做的,非常成熟的套路,可以学习学习。
ViewModel 1.x 中 ViewModelStore 的获取
public static ViewModelProvider of(@NonNull Fragment fragment) {
//...
return new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory);
}
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
//...
return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
}
我们可以看到,其主要其将核心逻辑抽取在了 ViewModelStores 中,接下来我以 FragmentActivity 为例继续追踪源码, Fragment 的逻辑大体相同,只是依赖于 childFragment 而已。
public static ViewModelStore of(@NonNull FragmentActivity activity) {
return holderFragmentFor(activity).getViewModelStore();
}
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
// 移交给 HolderFragmentManager 处理
return sHolderFragmentManager.holderFragmentFor(activity);
}
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
// 查找 FragmentManager 是否已经存在 HolderFragment
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
// 已经存在,直接返回
return holder;
}
// 因为 fragment commit 是个异步的过程,所以 FragmentManager 不存在可能是因为 commit 还没执行完成,因此需要在 mNotCommittedActivityHolders 里也存放一份。
holder = mNotCommittedActivityHolders.get(activity);
if (holder != null) {
return holder;
}
if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
// 添加 callback, 在 activity 销毁时移除 mNotCommittedActivityHolders 里的缓存, 一个应用只需添加一个 callback 足矣。
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
}
// 不存在,则创建,并存入 mNotCommittedActivityHolders 中
holder = createHolderFragment(fm);
mNotCommittedActivityHolders.put(activity, holder);
return holder;
}
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment();
// 添加 HOLDER_TAG, 便于寻找
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
private static HolderFragment findHolderFragment(FragmentManager manager) {
//...
// 通过 HOLDER_TAG 来寻找
Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
throw new IllegalStateException("Unexpected "
+ "fragment instance was returned by HOLDER_TAG");
}
return (HolderFragment) fragmentByTag;
}
这就是之前版本的做法,不过其实也可以通过 lifecycle 去做。有了 lifecycle 之后,这种做法就显得有些浪费了。但不管如何,这种对 Fragment 的应用也是值得称赞的。
查看原文: Android 架构之美 - ViewModel