转载自https://zhuanlan.zhihu.com/p/76361500

在页面(Activity/Fragment)很简单的情况下,通常我们会将UI交互,数据获取与处理等相关业务逻辑,全部写在页面中,但是在页面复杂的情况下,这样做是不合适的,它不符合“单一责任”原则。页面只应该负责接收用户的交互,以及将数据展示到屏幕上,相关数据应该单独存放和处理。

为此,Android为我们提供了ViewModel类,专门用于存放应用程序页面所需的数据。它将页面所需的数据从页面中剥离出来,页面只需要处理用户交互,以及负责展示数据的工作。

ViewModel将数据从Activity中剥离

另外,如果我们的应用程序支持横竖屏切换,当用户旋转手机屏幕时,我们还需要考虑数据的存储与恢复。如果数据不进行存储,那么通常我们还需要重新去获取一次。

而ViewModel能为我们解决这个问题,它独立于配置变化。也就是说,屏幕旋转导致的Activity重建,并不会影响到ViewModel的生命周期。

ViewModel这个名字可以这样理解:它是介于View(视图)和Model(模型数据)之间的这样一个东西,它起到了桥梁的作用,使得视图和数据既能够分离开,也能够保持通信。

Activity旋转重建与ViewModel生命周期之间的关系


接下去,我们一起来看看,如何在代码中实现一个ViewModel。

1.在应用程序的build.gradle中加入依赖。

implementation "android.arch.lifecycle:extensions:1.1.1"

2.写一个继承自ViewModel的类。

public class TimerViewModel extends ViewModel
{/*** 由于屏幕旋转导致的Activity重建,该方法不会被调用** 只有ViewModel已经没有任何Activity与之有关联,系统则会调用该方法,你可以在此清理资源* */@Overrideprotected void onCleared(){super.onCleared();}
}

ViewModel是一个抽象类,其中只有一个方法onCleared(),当ViewModel不再被需要的时候,也就是与之相关的Activity都被销毁时,该方法会被系统调用,我们可以在这个方法里面执行一些资源释放的操作,以免内存泄漏。

注意:既然ViewModel的销毁是由系统来判断和执行的,那么系统是如何判断的呢?是根据Context引用。因此,我们在使用ViewModel的时候,千万不能从外面传入Activity,Fragment或者View之类的含有Context引用的东西,否则系统会认为该ViewModel还在使用中,从而无法被系统销毁回收,导致内存泄漏的发生。

3.在页面中使用ViewModel。

TimerViewModel timerViewModel = ViewModelProviders.of(this).get(TimerViewModel.class);

ViewModel的实例化并不是通过普通的new来完成的,而是通过ViewModelProviders来完成。ViewModelProviders会去判断ViewModel是否存在,若存在则直接返回,否则它会去创建一个ViewModel。

4.前面我们提到,ViewModel最重要的作用是将界面和数据分离,并且独立于Activity的重建。为了验证这一点,我们在ViewModel中创建一个Timer计时器,每隔一秒钟,通过接口OnTimeChangeListener通知它的调用者。

public class TimerViewModel extends ViewModel
{private String TAG = this.getClass().getName();private Timer timer;private int currentSecond;/*** 开始计时* */public void startTiming(){if (timer == null){currentSecond = 0;timer = new Timer();TimerTask timerTask = new TimerTask(){@Overridepublic void run(){currentSecond++;if(onTimeChangeListener != null){onTimeChangeListener.onTimeChanged(currentSecond);}}};timer.schedule(timerTask, 1000, 1000);//延迟3秒执行}}/*** 通过接口的方式,完成对调用者的通知,这种方式不是太好,更好的方式是通过LiveData组件来实现* */public interface OnTimeChangeListener{void onTimeChanged(int second);}private OnTimeChangeListener onTimeChangeListener;public void setOnTimeChangeListener(OnTimeChangeListener onTimeChangeListener){this.onTimeChangeListener = onTimeChangeListener;}/*** 由于屏幕旋转导致的Activity重建,该方法不会被调用** 只有ViewModel已经没有任何Activity与之有关联,系统则会调用该方法,你可以在此清理资源* */@Overrideprotected void onCleared(){super.onCleared();Log.d(TAG, "onCleared()");timer.cancel();}
}

接着,在TimerActivity中监听OnTimeChangeListener发来的通知,并据此更新UI界面。

public class TimerActivity extends AppCompatActivity
{@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_timer);iniComponent();}private void iniComponent(){final TextView tvTime = findViewById(R.id.tvTime);//通过ViewModelProviders得到ViewModel,如果ViewModel不存在就创建一个新的,如果已经存在就直接返回已经存在的TimerViewModel timerViewModel = ViewModelProviders.of(this).get(TimerViewModel.class);timerViewModel.setOnTimeChangeListener(new TimerViewModel.OnTimeChangeListener(){@Overridepublic void onTimeChanged(final int second){//更新UI界面runOnUiThread(new Runnable(){@Overridepublic void run(){tvTime.setText("TIME:" + second);}});}});timerViewModel.startTiming();}
}

运行程序并旋转屏幕。可以看到,旋转屏幕导致Activity重建时,计时器并没有停止,也就是说,横竖屏状态下Activity对应的ViewModel是同一个。

使用ViewModel,不仅将界面和数据从代码上进行了分离,而且不再需要关心屏幕旋转带来的数据的丢失和获取问题。也许你会说onSaveInstanceState() 方法同样可以解决屏幕旋转带来的数据丢失问题,但它只能保存少量的能支持序列化的数据,而ViewModel没有这个限制,它能支持页面中所有的数据。但要注意的是,ViewModel不支持数据的持久化,当界面彻底销毁,ViewModel及其数据也就不存在了。

我们前面提到过,使用ViewModel的时候,不能将任何含有Context引用的对象传入ViewModel,因为这可能会导致内存泄露。但如果你希望在ViewModel中使用Context怎么办呢?我们可以使用AndroidViewModel类,它继承自ViewModel,并且接收Application作为Context,既然是Application作为Context,也就意味着,我们能够明确它的生命周期和Application是一样的,这就不算是一个内存泄露了。

另外,在本示例中,我们通过自定义接口的方式(OnTimeChangeListener)来实现ViewModel到Activity的通信,这不是一种好的方法,实际上Android为我们提供了LiveData组件来解决这个问题。通过LiveData,当ViewModel中的数据发生变化时,Activity能自动收到通知,从而更新UI。我们在下一章节中继续讨论这个问题。

ViewModel的基本使用相关推荐

  1. MVC 中的 ViewModel

    此文章总结自:http://rachelappel.com/use-viewmodels-to-manage-data-amp-organize-code-in-asp.net-mvc-applica ...

  2. 页面与ViewModel(上)

    在UWP淘宝与旺信中,笔者主要负责页面与控件的制作,这些工作看似简单,但要想做的全面细致仍然需要深入的思考.本文想分享一些在UWP旺信的制作过程中,笔者在UI页面与控件制作上体会到的一些心得.可能笔者 ...

  3. Android笔记之ViewModel的使用示例

    依赖 implementation 'android.arch.lifecycle:extensions:1.1.1' implementation 'com.squareup.retrofit2:r ...

  4. LiveData + ViewModel + Room (Google 官文)+Demo

    原文地址:lovestack.github.io/2017/11/13/- demo:github.com/lovestack/V- 本指南适用于那些过去构建应用程序有基础知识,现在想知道构建强大的生 ...

  5. Android JetPack ViewModel 源码解析

    是什么? ViewModel 用来存储页面相关的数据,当页面销毁的时候,存储数据也会清楚.但是当页面发生旋转的时候,并不会清楚数据. 怎么用? UserViewModel userViewModel ...

  6. android DataBind LiveData ViewModel 使用详解

    1.导入android x ,升级 版本到28 implementation 'androidx.appcompat:appcompat:1.0.0'def room_version = " ...

  7. 屏幕旋转导致Activity销毁重建,ViewModel是如何恢复数据的

    前言 当屏幕旋转或者切换系统语言时,Activity 生命周期从销毁再重建,但是ViewModel里面的变量值不受到影响,说明ViewModel中的变量在屏幕旋转前进行了存储,在屏幕旋转后又进行了恢复 ...

  8. Android Jetpack组件之ViewModel使用

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  9. 架构组件专栏 | ViewModel深入浅出

    本文是架构组件专栏的开篇文章,因此在文章开头我打算花些笔墨谈谈什么是架构组件以及我为什么打算写这个专栏. 谷歌官方为了帮助开发者加速开发并构建高质量的应用,推出了Jetpack.正如上图你所看到的,J ...

  10. LiveData ViewModel 使用详解

    什么是 LiveData LiveData 是一个可观测的数据持有类,但是不同于通常的被观察者,LiveData 具有生命周期感知能力.通俗点说,LiveData 就是具有 "Live&qu ...

最新文章

  1. 浙江大学医学院附属儿童医院倪艳组招聘博士后和科研助理-肠道微生物和代谢方向...
  2. linux把文件复制到压缩包里,Linux学习笔记(二十)文件压缩 zip压缩、tar打包、打包、解包...
  3. PHPWind的版权等信息去除的方法
  4. G代码 机器人的CNC实现
  5. python class函数报错_Python multiprocess pool模块报错pickling error问题解决方法分析
  6. AI单挑Dota 2世界冠军:被电脑虐哭……
  7. python ping 连接_Python检查ping终端的方法
  8. 中的live_2016知乎Live精选汇编——简历类
  9. 业务数据分析学习笔记--Lesson1
  10. Google APIs 学习/使用
  11. 天猫年货节助手、京东年货节助手,自动完成任务领取奖励,解放双手
  12. 基于FastICA的声源分离实例(matlab代码)
  13. 查询快递 教你一个方法按照物流途径城市筛选签收地
  14. 学习opengl官方指南 01 opengl介绍
  15. 广州刷脸支付骗局_刷脸支付骗局你仔细观察一下
  16. 小车运料c语言编程,西门子PLC编程实例及技巧(运料小车控制系统)
  17. [论文阅读]Using the Output Embedding to Improve Language Models
  18. 多层高速PCB设计学习笔记(五)四层板实战(下)之阻抗控制计算(SI9000)
  19. C++【算法】【动态规划问题】
  20. 微信车辆派遣小程序实现小结

热门文章

  1. 6位中国民间艺术家在美国纽约法拉盛图书馆庆猪年
  2. java骰子_AcWing 80. 骰子的点数,骰子的点数的概率——Java实现简单易懂
  3. Javascript获取当前时间和日期的方法
  4. Aseprite终极白嫖教程
  5. 手机销售系统设计与实现
  6. Deflater与Inflater的压缩与解压缩
  7. 100分钟吃掉DIN深度兴趣网络
  8. 基于 Logistic 混沌映射和 Arnold 变换 的变换域水印改进算法【高级网络与信息安全技术-信息隐藏期末课程论文】
  9. npm 安装出现的问题:一定要以管理员身份运行cmd
  10. 华为emui11是不是鸿蒙,emui11是不是鸿蒙系统