1. 概述
  2.  
  3. ViewModel,从字面上理解的话,它肯定是跟视图(View)以及数据(Model)相关的。正像它字面意思一样,它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类,也就是说ViewModel是用来管理UI相关的数据的,同时ViewModel还可以用来负责UI组件间的通信。
  4. 之前存在的问题
  5.  
  6. ViewModel用来存储和管理UI相关的数据,可于将一个ActivityFragment组件相关的数据逻辑抽象出来,并能适配组件的生命周期,如当屏幕旋转Activity重建后,ViewModel中的数据依然有效。
  7.  
  8. 引入ViewModel之前,存在如下几个问题:
  9.  
  10. 通常Android系统来管理UI controllers(如ActivityFragment)的生命周期,由系统响应用户交互或者重建组件,用户无法操控。当组件被销毁并重建后,原来组件相关的数据也会丢失,如果数据类型比较简单,同时数据量也不大,可以通过onSaveInstanceState()存储数据,组件重建之后通过onCreate(),从中读取Bundle恢复数据。但如果是大量数据,不方便序列化及反序列化,则上述方法将不适用。
  11. UI controllers经常会发送很多异步请求,有可能会出现UI组件已销毁,而请求还未返回的情况,因此UI controllers需要做额外的工作以防止内存泄露。
  12. Activity因为配置变化而销毁重建时,一般数据会重新请求,其实这是一种浪费,最好就是能够保留上次的数据。
  13. UI controllers其实只需要负责展示UI数据、响应用户交互和系统交互即可。但往往开发者会在ActivityFragment中写许多数据请求和处理的工作,造成UI controllers类代码膨胀,也会导致单元测试难以进行。我们应该遵循职责分离原则,将数据相关的事情从UI controllers中分离出来。
  14.  
  15. ViewModel基本使用
  16.  
  17. public class MyViewModel extends ViewModel {
  18. private MutableLiveData<List<User>> users;
  19. public LiveData<List<User>> getUsers() {
  20. if (users == null) {
  21. users = new MutableLiveData<List<Users>>();
  22. loadUsers();
  23. }
  24. return users;
  25. }
  26.  
  27. private void loadUsers() {
  28. // 异步调用获取用户列表
  29. }
  30. }
  31.  
  32. 新的Activity如下:
  33.  
  34. public class MyActivity extends AppCompatActivity {
  35. public void onCreate(Bundle savedInstanceState) {
  36. MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
  37. model.getUsers().observe(this, users -> {
  38. // 更新 UI
  39. });
  40. }
  41. }
  42.  
  43. 如果Activity被重新创建了,它会收到被之前Activity创建的相同MyViewModel实例。当所属Activity终止后,框架调用ViewModelonCleared()方法清除资源。
  44.  
  45. 因为ViewModel在指定的ActivityFragment实例外存活,它应该永远不能引用一个View,或持有任何包含Activity context引用的类。如果ViewModel需要Applicationcontext(如获取系统服务),可以扩展AndroidViewmodel,并拥有一个构造器接收Application
  46.  
  47. Fragment间共享数据
  48.  
  49. 一个Activity中的多个Fragment相互通讯是很常见的。之前每个Fragment需要定义接口描述,所属Activity将二者捆绑在一起。此外,每个Fragment必须处理其他Fragment未创建或不可见的情况。通过使用ViewModel可以解决这个痛点,这些Fragment可以使用它们的Activity共享ViewModel来处理通讯:
  50.  
  51. public class SharedViewModel extends ViewModel {
  52. private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
  53.  
  54. public void select(Item item) {
  55. selected.setValue(item);
  56. }
  57.  
  58. public LiveData<Item> getSelected() {
  59. return selected;
  60. }
  61. }
  62.  
  63. public class MasterFragment extends Fragment {
  64. private SharedViewModel model;
  65. public void onActivityCreated() {
  66. model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
  67. itemSelector.setOnClickListener(item -> {
  68. model.select(item);
  69. });
  70. }
  71. }
  72.  
  73. public class DetailFragment extends LifecycleFragment {
  74. public void onActivityCreated() {
  75. SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
  76. model.getSelected().observe(this, { item ->
  77. // update UI
  78. });
  79. }
  80. }
  81.  
  82. 注意:上面两个Fragment都用到了如下代码来获取ViewModelgetActivity()返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个SharedViewModel对象。
  83.  
  84. SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
  85.  
  86. 1
  87.  
  88. 这种方式的好处包括:
  89.  
  90. Activity不需要做任何事情,也不需要知道通讯的事情
  91. Fragment不需要知道彼此,除了SharedViewModel进行联系。如果它们(Fragment)其中一个消失了,其余的仍然能够像往常一样工作。
  92. 每个Fragment有自己的生命周期,而且不会受其它Fragment生命周期的影响。事实上,一个Fragment替换另一个FragmentUI的工作也不会受到任何影响。
  93.  
  94. ViewModel的生命周期
  95.  
  96. ViewModel对象的范围由获取ViewModel时传递至ViewModelProviderLifecycle所决定。ViewModel始终处在内存中,直到Lifecycle永久地离开—对于Activity来说,是当它终止(finish)的时候,对于Fragment来说,是当它分离(detached)的时候。
  97. 这里写图片描述

  98. 上图左侧为Activity的生命周期过程,期间有一个旋转屏幕的操作;右侧则为ViewModel的生命周期过程。
  99.  
  100. 一般通过如下代码初始化ViewModel
  101.  
  102. viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);
  103.  
  104. 1
  105.  
  106. this参数一般为ActivityFragment,因此ViewModelProvider可以获取组件的生命周期。
  107.  
  108. Activity在生命周期中可能会触发多次onCreate(),而ViewModel则只会在第一次onCreate()时创建,然后直到最后Activity销毁。
  109. ViewModel相关类图
  110.  
  111. 借用Android架构组件(三)——ViewModel的类图:
  112. 这里写图片描述
  113.  
  114. ViewModelProvidersViewModel工具类,该类提供了通过FragmentActivity得到ViewModel的方法,而具体实现又是由ViewModelProvider实现的。
  115.  
  116. ViewModelProvider是实现ViewModel创建、获取的工具类。在ViewModelProvider中定义了一个创建ViewModel的接口类——FactoryViewModelProvider中有个ViewModelStore对象,用于存储ViewModel对象。
  117.  
  118. ViewModelStore是存储ViewModel的类,具体实现是通过HashMap来保存ViewModle对象。
  119.  
  120. ViewModel是个抽象类,里面只定义了一个onCleared()方法,该方法在ViewModel不在被使用时调用。ViewModel有一个子类AndroidViewModel,这个类是便于要在ViewModel中使用Context对象,因为我们前面提到不能在ViewModel中持有Activity的引用。
  121.  
  122. ViewModelStoresViewModelStore的工厂方法类,它会关联HolderFragmentHolderFragment有个嵌套类——HolderFragmentManager
  123.  
  124. ViewModel相关时序图
  125.  
  126. 追溯创建一个ViewModel的源码,会察觉需要的步骤有点多。下面以在Fragment中得到ViewModel对象为例看下整个过程的时序图。
  127. 借用Android架构组件(三)——ViewModel的时序图:
  128. 这里写图片描述
  129.  
  130. 时序图看起来比较复杂,但是它只描述了两个过程:
  131.  
  132. 得到ViewModel对象。
  133. HolderFragment被销毁时,ViewModel收到onCleared()通知。
  134.  
  135. ViewModel相关源码分析
  136.  
  137. ViewModelProviders类的具体实现:
  138.  
  139. public class ViewModelProviders {
  140.  
  141. private static Application checkApplication(Activity activity) {
  142. Application application = activity.getApplication();
  143. if (application == null) {
  144. throw new IllegalStateException("Your activity/fragment is not yet attached to "
  145. + "Application. You can't request ViewModel before onCreate call.");
  146. }
  147. return application;
  148. }
  149.  
  150. private static Activity checkActivity(Fragment fragment) {
  151. Activity activity = fragment.getActivity();
  152. if (activity == null) {
  153. throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
  154. }
  155. return activity;
  156. }
  157.  
  158. @NonNull
  159. @MainThread
  160. public static ViewModelProvider of(@NonNull Fragment fragment) {
  161. ViewModelProvider.AndroidViewModelFactory factory =
  162. ViewModelProvider.AndroidViewModelFactory.getInstance(
  163. checkApplication(checkActivity(fragment)));
  164. return new ViewModelProvider(ViewModelStores.of(fragment), factory);
  165. }
  166.  
  167. @NonNull
  168. @MainThread
  169. public static ViewModelProvider of(@NonNull FragmentActivity activity) {
  170. ViewModelProvider.AndroidViewModelFactory factory =
  171. ViewModelProvider.AndroidViewModelFactory.getInstance(
  172. checkApplication(activity));
  173. return new ViewModelProvider(ViewModelStores.of(activity), factory);
  174. }
  175.  
  176. @NonNull
  177. @MainThread
  178. public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
  179. checkApplication(checkActivity(fragment));
  180. return new ViewModelProvider(ViewModelStores.of(fragment), factory);
  181. }
  182.  
  183. @NonNull
  184. @MainThread
  185. public static ViewModelProvider of(@NonNull FragmentActivity activity,
  186. @NonNull Factory factory) {
  187. checkApplication(activity);
  188. return new ViewModelProvider(ViewModelStores.of(activity), factory);
  189. }
  190.  
  191. ViewModelProviders提供了四个of()方法,四个方法功能类似,其中of(FragmentActivity activity, Factory factory)和of(Fragment fragment, Factory factory)提供了自定义创建ViewModel的方法。
  192. 1. 判断Fragment的是否Attached to ActivityActivityApplication对象是否为空。
  193. 2. 创建ViewModel对象看似很简单,一行代码搞定。
  194.  
  195. new ViewModelProvider(ViewModelStores.of(fragment), factory)
  196.  
  197. 先看看ViewModelStores.of()方法:
  198.  
  199. @NonNull
  200. @MainThread
  201. public static ViewModelStore of(@NonNull Fragment fragment) {
  202. if (fragment instanceof ViewModelStoreOwner) {
  203. return ((ViewModelStoreOwner) fragment).getViewModelStore();
  204. }
  205. return holderFragmentFor(fragment).getViewModelStore();
  206. }
  207.  
  208. 继续深入发现其实是实现了一个接口:
  209.  
  210. public interface ViewModelStoreOwner {
  211. @NonNull
  212. ViewModelStore getViewModelStore();
  213. }
  214.  
  215. holderFragmentFor()是HolderFragment的静态方法,HolderFragment继承自Fragment。我们先看holderFragment()方法的具体实现
  216.  
  217. @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
  218. public static HolderFragment holderFragmentFor(Fragment fragment) {
  219. return sHolderFragmentManager.holderFragmentFor(fragment);
  220. }
  221.  
  222. @NonNull
  223. @Override
  224. public ViewModelStore getViewModelStore() {
  225. return mViewModelStore;
  226. }
  227.  
  228. 继续看HolderFragmentManager.holderFragmentFor()方法的具体实现
  229.  
  230. HolderFragment holderFragmentFor(Fragment parentFragment) {
  231. FragmentManager fm = parentFragment.getChildFragmentManager();
  232. HolderFragment holder = findHolderFragment(fm);
  233. if (holder != null) {
  234. return holder;
  235. }
  236. holder = mNotCommittedFragmentHolders.get(parentFragment);
  237. if (holder != null) {
  238. return holder;
  239. }
  240.  
  241. parentFragment.getFragmentManager()
  242. .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
  243. holder = createHolderFragment(fm);
  244. mNotCommittedFragmentHolders.put(parentFragment, holder);
  245. return holder;
  246. }
  247.  
  248. private FragmentLifecycleCallbacks mParentDestroyedCallback =
  249. new FragmentLifecycleCallbacks() {
  250. @Override
  251. public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {
  252. super.onFragmentDestroyed(fm, parentFragment);
  253. HolderFragment fragment = mNotCommittedFragmentHolders.remove(
  254. parentFragment);
  255. if (fragment != null) {
  256. Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
  257. }
  258. }
  259. };
  260.  
  261. private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
  262. HolderFragment holder = new HolderFragment(); // 创建HolderFragment对象
  263. fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
  264. return holder;
  265. }
  266.  
  267. public HolderFragment() {
  268. //这个是关键,这就使得Activity被recreate时,Fragment的onDestroy()和onCreate()不会被调用
  269. setRetainInstance(true);
  270. }
  271.  
  272. setRetainInstance(boolean) Fragment中的一个方法。将这个方法设置为true就可以使当前FragmentActivity重建时存活下来, 如果不设置或者设置为 false, 当前 Fragment 会在 Activity 重建时同样发生重建, 以至于被新建的对象所替代。
  273.  
  274. setRetainInstance(boolean)为true Fragment 中放一个专门用于存储ViewModelMap, 自然Map中所有的ViewModel都会幸免于Activity重建,让Activity, Fragment都绑定一个这样的Fragment, ViewModel存放到这个 Fragment Map 中, ViewModel 组件就这样实现了。
  275.  
  276. 到此为止,我们已经得到了ViewStore对象,前面我们在创建ViewModelProvider对象是通过这行代码实现的new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory)现在再看下ViewModelProvider的构造方法
  277.  
  278. public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
  279. mFactory = factory;
  280. this.mViewModelStore = store;
  281. }
  282.  
  283. 现在就可以通过ViewModelProvider.get()方法得到ViewModel对象,继续看下该方法的具体实现
  284.  
  285. @NonNull
  286. @MainThread
  287. public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
  288. ViewModel viewModel = mViewModelStore.get(key); //从缓存中查找是否有已有ViewModel对象。
  289.  
  290. if (modelClass.isInstance(viewModel)) {
  291. //noinspection unchecked
  292. return (T) viewModel;
  293. } else {
  294. //noinspection StatementWithEmptyBody
  295. if (viewModel != null) {
  296. // TODO: log a warning.
  297. }
  298. }
  299.  
  300. viewModel = mFactory.create(modelClass); //创建ViewModel对象,然后缓存起来。
  301. mViewModelStore.put(key, viewModel);
  302. //noinspection unchecked
  303. return (T) viewModel;
  304. }
  305.  
  306. ViewModelProvider.get()方法比较简单,注释中都写明了。最后我们看下ViewModelStore类的具体实现
  307.  
  308. public class ViewModelStore {
  309.  
  310. private final HashMap<String, ViewModel> mMap = new HashMap<>();
  311.  
  312. final void put(String key, ViewModel viewModel) {
  313. ViewModel oldViewModel = mMap.get(key);
  314. if (oldViewModel != null) {
  315. oldViewModel.onCleared();
  316. }
  317. mMap.put(key, viewModel);
  318. }
  319.  
  320. final ViewModel get(String key) {
  321. return mMap.get(key);
  322. }
  323.  
  324. public final void clear() {
  325. for (ViewModel vm : mMap.values()) {
  326. vm.onCleared();
  327. }
  328. mMap.clear();
  329. }
  330. }
  331.  
  332. ViewModelStore是缓存ViewModel的类,put()、get()方法用于存取ViewModel对象,另外提供了clear()方法用于清空缓存的ViewModel对象,在该方法中会调用ViewModel.onCleared()方法通知ViewModel对象不再被使用。
  333.  
  334. ViewModel收到onCleared()通知
  335. HolderFragmentonDestroy()方法
  336.  
  337. @Override
  338. public void onDestroy() {
  339. super.onDestroy();
  340. mViewModelStore.clear();
  341. }
  342.  
  343. onDestroy()方法中调用了ViewModelStore.clear()方法,我们知道在该方法中会调用ViewModelonCleared()方法。在你看了HolderFragment源码后,或许你会有个疑问,mViewModelStore保存的ViewModel对象是在哪里添加的呢? 细心的话,你会发现在ViewModelProvider的构造方法中,已经将HolderFragment中的ViwModelStore对象mViewModelStore的引用传递给了ViewModelProvider中的mViewModelStore,而在ViewModelProvider.get()方法中会向mViewModelStore添加ViewModel对象。
  344. 总结
  345.  
  346. ViewModel职责是为ActivityFragment管理、请求数据,具体数据请求逻辑不应该写在ViewModel中,否则ViewModel的职责会变得太重,此处需要一个引入一个Repository,负责数据请求相关工作。具体请参考 Android架构组件。
  347. ViewModel可以用于Activity内不同Fragment的交互,也可以用作Fragment之间一种解耦方式。
  348. ViewModel也可以负责处理部分Activity/Fragment与应用其他模块的交互。
  349. ViewModel生命周期(以Activity为例)起始于Activity第一次onCreate(),结束于Activity最终finish时。
  350.  
  351. 官方:
  352. https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html
  353. ————————————————
  354. 版权声明:本文为CSDN博主「Double-Smile」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
  355. 原文链接:https://blog.csdn.net/qq_24442769/article/details/79426609

Android架构组件——ViewModel的更多相关文章

  1. 改造 Android 官方架构组件 ViewModel

    前言 Android 官方架构组件在今年 5 月份 Google I/O 大会上被公布, 直到 11 月份一直都是测试版, 由于工作比较繁忙, 期间我只是看过类似的文章, 但没有在实际项目中使用过, ...

  2. Android Jetpack组件 - ViewModel,LiveData使用以及原理

    本文涉及的源码版本如下: com.android.support:appcompat-v7:27.1.1 android.arch.lifecycle:extensions:1.1.1 android ...

  3. Android 架构组件-Lifecycle、LiveData、ViewModel

    Lifecycle Lifecycle组件包括LifecycleOwner.LifecleObserver,能方便监听Activity或者Fragment的生命周期. 步骤: 1.实现Lifecycl ...

  4. Android 架构组件 Room 介绍及使用

    关于Room Room是Google官方提供的数据库ORM框架,使用起来非常方便.Room在SQLite上提供了一个抽象层,以便在利用SQLite的全部功能的同时能更加流畅的访问数据库. Room中三 ...

  5. Android官方架构组件介绍之LifeCycle

    Google 2017 I/O开发者大会于近日召开,在开发者大会上谷歌除了发布了Android O等一些新产品之外,也对Android代码的架构做出了一个官方的回应. Google 2017 I/O开 ...

  6. Jetpack 架构组件 LiveData ViewModel MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  7. Android官方架构组件指南

    此指南适用于那些曾经或现在进行Android应用的基础开发,并希望了解和学习编写Android程序的最佳实践和架构.通过学习来构建强大的生产级别的应用. 注意:此指南默认你对Android开发有比较深 ...

  8. Android官方架构组件介绍之LifeCycle(一)

    Android官方架构组件介绍之LifeCycle 下面是官方提供的Android App开发的架构图: 从上图可以看到一些关键字:ViewModel,LiveData,Room等.其实看了上面视频的 ...

  9. android物理动画、Kotlin客户端、架构组件、菜单效果、应用选择器等源码

    Android精选源码 Android一个有趣的Android动画交互设计 android可伸缩日历效果源码 关于界面,全新的卡片风格,支持夜晚模式 Android 用 Kotlin 实现的基于物理的 ...

随机推荐

  1. typescript - 5.接口

    接口的作用: 在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用.接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不 ...

  2. centos7.6环境编译安装php-7.2.24修复最新 CVE-2019-11043 漏洞

    先编译安装php-7.2.24,然后编译安装扩展 主版本地址地址:https://www.php.net/distributions/php-7.2.24.tar.gz # 编译 php-7.2.24 ...

  3. jenkins报错Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password) 的处理

    问题背景:jenkins服务器发布代码后需要执行删除缓存的操作ssh -p222 eus_pe_devadmin@1.1.1.1 "sudo rm -rf /dev/shm/nginx/hi ...

  4. Blob/DataURL/canvas/image的相互转换

    函数都比较简单,直接看就ok了 /*-----------------------------------------------------------------------*/ // canva ...

  5. jwplayer :若请求不到流,则页面一直转圈请求效果

    思路: 利用jwplayer onPlay(播放) .onError(出错)事件. 页面:背景图为黑色,嵌入一张背景为黑色的 git 动态图,加载页面时隐藏. 流程:若进入到onPlay 方法,则说明 ...

  6. redis密码配置

    配置密码 重启密码会失效 配置在redis.conf中 requirepass test123,则重启不会失效

  7. IDEA 2018 搭建 Spring MVC helloworld

    转自https://segmentfault.com/a/1190000017248622 网上看了不少idea搭建SpringMVC Helloworld的例子,但是一个个试下来都没有成功.我把他们 ...

  8. 用ASP.NET Core 2.1 建立规范的 REST API -- 保护API和其它(总结)

    本文介绍如何保护API,无需看前边文章也能明白吧. 预备知识: http://www.cnblogs.com/cgzl/p/9010978.html http://www.cnblogs.com/cg ...

  9. LumiSoft 邮件操作删除(无法删除解决方法)

    最近在用 LumiSoft  进行邮件读取,然后操作相关附件邮件使用的是qq邮箱,读取后进行移除,但是怎么都移除不了 后来咨询了官方客服,原来是设置不对 需要 取消掉 X禁止收信软件删信 (仅对 PO ...

  10. 从一个案例窥探ORACLE的PASSWORD_VERSIONS

    1.环境说明 ORACLE 客户端版本 11.2.0.1 ORACLE 服务端版本 12.2.0.1 2.异常现象 客户端(下文也称为Cp)访问服务端(Sp),报了一个错误: Figure 1 以错误 ...