前言 首先祝大家在新的一年中,身体健康,心想事成。
算了下,去年技术类博客写了 15 篇,看下数量还算比较满意,可是基本都是前半年写的,那时候刚刚下定决心要好好写博客,后半年因为工作上还有自己偷懒心理,基本都没怎么写了,真是惭愧。
不过经过去年写了这些文章,慢慢也学习到了一些写技术文章上的技巧,希望今年能方得始终,提升文章的质量。
关于 Jetpack 相信已经有不少人对 Google 推出的 Jetpack 系列组件都有所耳闻,现在网上已经有不少的分析文章了,涵盖了用法和源码解析,所以我就不重复造轮子,在这个系列文章中,不会涉及使用教程等,只写一些个人的使用体会,也当作给自己做笔记。
关于本文 这篇文章主要讲 Jetpack 中的 ViewModel
ViewModel
The ViewModel
class is designed to store and manage UI-related data in a lifecycle conscious way.
简而言之,就是在生命周期中管理数据。说到生命周期,我们都知道 Android 中 Activity
和 Fragment
都有各自对应的生命周期,比如 Activity
,它的生命周期如下图:
通常我们会在 onCreate()
初始化数据,在 onResume()
中展示数据,onDestroy()
中释放数据,类似下面这些伪代码:
1 2 3 4 5 6 7 8 9 10 11 onCreate() { initData(); } onResume() { displayData() } onDestory() { recycleData(); }
如果没有在 onDestory()
中及时释放某些资源,可能还会导致内存泄漏,这是第一个问题。
第二个问题,Android 系统可能会在内存不足的情况下,回收了 Activity
,导致 Activity
重建时数据会丢失,对于这种情况,Android 提供了 onSaveInstanceState
中保存数据,在 onRestoreInstanceState
和 onCreate
中获取。类似下面这些伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 onSaveInstanceState(Bundle outState) { outState.putData(); } onRestoreInstanceState(Bundle outState) { outState.getData(); } onCreate(Bundle savedInstanceState) { if (savedInstanceState != null ) { savedInstanceState.getData(); } }
这种方式除了重复的胶水代码以外,还存在 Bundle
存储只适用于支持序列化(Serializable 和 Parcelable)的少量数据,当然除了使用 Android SDK 提供的这种方案以外,我们也可以自己实现类似的方案,类似以下伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Map<String,Data> map; getData(String id) { if (map.get(id) == null ){ Data data = createData(); map.put(id,data); } return map.get(id); } removeData(String id) { map.removeByKey(id); } onCreate() { getData(this ); } onDestory() { if (!isChangingConfigurations){ removeData(this ); } }
看了上面解决思路后,我们再来看看 Google 提供的 ViewModel
,它是如何解决上面提到的两个问题的。首先看下,ViewModel
的典型用法,来源于官方文档:
首先定义一个 MyViewModel
继承于 ViewModel
,这里使用 LiveData
作为数据源,这里我们只需要知道 LiveData
和 RxJava
是差不多的东西就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class MyViewModel extends ViewModel { private MutableLiveData<List<User>> users; public LiveData<List<User>> getUsers () { if (users == null ) { users = new MutableLiveData <List<User>>(); loadUsers(); } return users; } private void loadUsers () { } }
定义好 ViewModel
之后,我们在 Activity
中使用它:
1 2 3 4 5 6 7 8 9 10 11 public class MyActivity extends AppCompatActivity { public void onCreate (Bundle savedInstanceState) { MyViewModel model = ViewModelProviders.of(this ).get(MyViewModel.class); model.getUsers().observe(this , users -> { }); } }
我们先看下 ViewModelProviders.of
的函数签名:
1 2 3 4 5 public static ViewModelProvider of (@NonNull Fragment fragment) ;public static ViewModelProvider of (@NonNull FragmentActivity activity) ;public static ViewModelProvider of (@NonNull Fragment fragment, @Nullable Factory factory) ;public static ViewModelProvider of (@NonNull FragmentActivity activity, @Nullable Factory factory) ;
需要将当前 Activity
或 Fragment
作为参数,这也是 ViewModel
将数据与生命周期结合起来的地方。那具体也是如何实现的呢?
1 2 3 4 5 6 7 8 9 10 @NonNull @MainThread 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); }
factory
顾名思义就是 ViewModel
的工厂类,这里默认使用 AndroidViewModelFactory
最后返回 ViewModelProvider
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @NonNull @MainThread public <T extends ViewModel > T get (@NonNull String key, @NonNull Class<T> modelClass) { ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { return (T) viewModel; } else { if (viewModel != null ) { } } viewModel = mFactory.create(modelClass); mViewModelStore.put(key, viewModel); return (T) viewModel; } @NonNull @Override public <T extends ViewModel > T create (@NonNull Class<T> modelClass) { if (AndroidViewModel.class.isAssignableFrom(modelClass)) { try { return modelClass.getConstructor(Application.class).newInstance(mApplication); } catch (NoSuchMethodException e) { throw new RuntimeException ("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException ("Cannot create an instance of " + modelClass, e); } catch (InstantiationException e) { throw new RuntimeException ("Cannot create an instance of " + modelClass, e); } catch (InvocationTargetException e) { throw new RuntimeException ("Cannot create an instance of " + modelClass, e); } } return super .create(modelClass); }
我们将以上两段代码合起来看,其实逻辑是比较清晰的:
Factor
的实现可以通过反射来实现,比如默认的 AndroidViewModelFactory
会优先调用使用 Application
作为参数的构造方法,来创建实例。所以,如果自定义的 ViewModel
构造方法有其他参数,就需要自定义 Factor
而 ViewModelStore
则是 Activity
重建时还能拥有之前数据的保障。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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); } public final void clear () { for (ViewModel vm : mMap.values()) { vm.onCleared(); } mMap.clear(); }
ViewModelStore
的源码很短,可以看到其实就是使用 HashMap
作为数据载体。既然有使用,那就需要清理操作,可以看到有个 clear
它会清除 HashMap
中的缓存数据。我们首先看下 ViewModelProvider
中的 mViewModelStore
是在哪里赋值的:
1 2 3 4 5 6 7 8 public ViewModelProvider (@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) { this (owner.getViewModelStore(), factory); } public ViewModelProvider (@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; this .mViewModelStore = store; }
通过 ViewModelStoreOwner.getViewModelStore
获取 ViewModelStore
实例对象,而 ViewModelStoreOwner
实际就是我们调用 ViewModelProviders.of
中传递的 FragmentActivity
和 Fragment
FragmentActivity
中会通过 onRetainNonConfigurationInstance
和 getLastNonConfigurationInstance
去保持 Activity
因为屏幕旋转等配置发生改变而导致重建时,数据的唯一性
而 Fragment
和 FragmentActivity
会在 onDestory
时判断是否需要调用 ViewModelStore.clear
1 2 3 4 5 6 7 8 9 10 @Override protected void onDestroy () { super .onDestroy(); if (mViewModelStore != null && !isChangingConfigurations()) { mViewModelStore.clear(); } mFragments.dispatchDestroy(); }
讲完 ViewModelStore
的缓存功能之后,我们再来看下,ViewModelProvider.of
不同签名的方法:
1 2 3 4 5 6 7 8 9 10 11 @NonNull @MainThread public static ViewModelProvider of (@NonNull Fragment fragment) { return of(fragment, null ); } @NonNull @MainThread public static ViewModelProvider of (@NonNull FragmentActivity activity) { return of(activity, null ); }
这里需要注意的是,ViewModel
在不同作用域下的实例,首先,FragmentActivity
和 Fragment
都实现了 ViewModelStoreOwner
即它们都有各自的 ViewModelStore
简单来说,如果想在 Fragment
中获取依附的 Activity
的 ViewModel
实例,那需要使用 of(FragmentActivity)
的方法,这也是一种 Activity
和 Fragment
之间通信的好方式。
总结 现在我们来比较下自定义实现的方案和 ViewModel
,可以发现其实核心思想是共通,首先我们需要一个保存于 Activity
和 Fragment
生命周期之外的存储空间,在 ViewModel
中是 ViewModelStore
,其次我们需要在 Activity
和 Fragment
对应的生命周期中,去初始化和清理这个 ViewModelStore