ViewModel 是什么
ViewModel 类旨在以注重生命周期的方式 存储和管理 界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。
为什么ViewModel 类中的数据可在发生屏幕旋转等配置更改后继续留存?
因为ViewModel的生命周期长于组件(Activity/Fragment)的生命周期
下图是左侧给出了Activity 经历屏幕旋转而后结束的过程,所处的各种生命周期状态。而右侧是ViewModel的生命周期。
从中可以看出ViewModel的生命周期是长于组件(Activity)的生命周期。

屏幕旋转后数据得以保存仅是ViewModel的其中一个优势,生命周期比组件长的优势还可以:
- 减少资源浪费:避免因配置更改而重新创建对象
- 避免内存泄露:界面控制器经常需要做异步调用,而异步调用需要在一段时间后才返回结果。如果界面关闭之后数据在还没返回,且其中的一些引用还存在,那么内存泄露就不可避免了。通常我们会在页面关闭的时候手动清除引用。在生命周期中我们可以看到
ViewModel还有一个方法onCleared(),我们可在该方法手动清除引用。

谷歌在生命感知组件的最佳做法中,推荐我们使用ViewModel+LiveData的组合。

既然如此,那我们先来了解何为LiveData。
LiveData 是什么
LiveData是一种 可观察的数据存储器类。与常规的可观察类不同,LiveData具有生命周期感知能力,所以它能在不同生命周期处理不同的操作。
由于LiveData是可观察的数据存储类且有生命周期感知能力,因此它有具备如下优势:
-
确保界面符合数据状态:LiveData 遵循观察者模式。当生命周期改变数据也会刷新
-
不会发生内存泄露:观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
-
不会因 Activity 停止而导致崩溃:如果观察者的生命周期处于非活跃状态,则它不会接收任何 LiveData 事件。
-
不再需要手动处理生命周期:LiveData 将自动管理所有这些操作
-
数据始终保持最新状态:如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。
-
适当的配置更改:如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
-
共享数据:数据存储在
ViewModel中,需要相应资源的任何(Activity/Fragment)等观察者只需观察LiveData对象
ViewModel+LiveData 使用步骤
示例:
(1)实现倒计时功能
(2)ViewModel传参获取数据库的数据展示
Module -> build.gradle的引入
版本依赖查看:https://developer.android.google.cn/jetpack/androidx/releases/lifecycle#declaring_dependencies
1 def lifecycle_version = "2.2.0" 2 3 // ViewModel 4 implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" 5 // LiveData 6 implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" 7 // 还有一些可选项,可查看上面的版本依赖链接
2. 创建Viewmodel(推荐创建BaseViewModel,之后再创建具体的ViewModel继承BaseViewModel)
1/** 2 * ViewModel 基类 3 */ 4open class BaseViewModel : ViewModel() { 5 override fun onCleared() { 6 super.onCleared() 7 Log.e("CommonViewModel","onCleared") 8 } 9} 10 11(示例1:倒计时功能) 12 13class MineViewModel : BaseViewModel() { 14 private val periodTime = 1000L 15 private val mElapsedRealTime = MutableLiveData<Long>() 16 private var mInitialTime: Long = 0 17 18 // Create a LiveData with a Long 19 val countDownTime: MutableLiveData<Long> by lazy { 20 mElapsedRealTime 21 } 22 /** 23 * 开启倒计时 24 */ 25 fun getTime(){ 26 mInitialTime = SystemClock.elapsedRealtime() 27 val timer = Timer() 28 val timeTask = object : TimerTask() { 29 override fun run() { 30 val newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000 31 if (newValue < 10){ 32 mElapsedRealTime.postValue(newValue) 33 }else{ 34 timer.cancel() 35 } 36 } 37 } 38 timer.scheduleAtFixedRate(timeTask,periodTime,periodTime) 39 } 40} 41 42 43(示例2:ViewModel传参获取数据库的数据展示) 44 45class HomeViewModel(context: Context): BaseViewModel() { 46 companion object { 47 private const val PAGE_SIZE = 15 48 private const val ENABLE_PLACEHOLDERS = false 49 } 50 val mContext = context 51 val dao = StudentDb.get(mContext).studentDao() 52 53 val allStudents = LivePagedListBuilder(dao.getAllStudent(), PagedList.Config.Builder() 54 .setPageSize(PAGE_SIZE) //配置分页加载的数量 55 .setEnablePlaceholders(ENABLE_PLACEHOLDERS) //配置是否启动PlaceHolders 56 .setInitialLoadSizeHint(PAGE_SIZE) //初始化加载的数量 57 .build()).build() 58}
3. 创建 Activity(推荐Fragment)中使用
1(示例1:倒计时功能) 2class MineFragment : BaseFragment(),MineContract.View { 3 4 private lateinit var viewModel: MineViewModel 5 6 override fun initData() { 7 // 创建实例 8 viewModel = ViewModelProviders.of(this).get(MineViewModel::class.java) 9 // 观察数据 10 viewModel.countDownTime.observe(this, Observer<Long> { aLong -> 11 //Update UI 12 tv_name.text = "time = " + aLong!! 13 }) 14 } 15 override fun initView() { 16 // 订阅事件 17 lifecycle.addObserver(minePresenter) 18 // 获取数据 19 btn_get_data.setOnClickListener { 20 viewModel.getTime() 21 } 22 } 23 // 省略部分代码,具体可看下方源码链接 24} 25 26(示例2:ViewModel传参获取数据库的数据展示) 27class HomeJetpackFragment : BaseFragment() { 28 private val TAG = HomeJetpackFragment::class.java.simpleName 29 30 private val viewModel by lazy(LazyThreadSafetyMode.NONE) { 31 ViewModelProviders.of(this, object : ViewModelProvider.Factory { 32 // 传递 context 33 override fun <T : ViewModel?> create(modelClass: Class<T>): T = HomeViewModel( 34 BaseApplication.context as Application 35 ) as T 36 }).get(HomeViewModel::class.java) 37 } 38 39 override fun initView() { 40 val adapter = StudentAdapter() 41 val layoutManager = LinearLayoutManager(activity) 42 rv_list.layoutManager = layoutManager 43 rv_list.adapter = adapter 44 // 将数据的变化反映到UI上 45 viewModel.allStudents.observe(this, Observer { 46 adapter.submitList(it) 47 }) 48 } 49 // 省略部分代码,具体可看下方源码链接 50}
Tip:
initData 和 initView 执行顺序如下:

详细使用代码请参见:YGragon/FrameDemo
总结
使用ViewModel+LiveData的模式,可以很好的在组件的不同生命周期处理数据。这是由于LiveData是生命周期感知型类所带来的特性。ViewModel中专注于处理数据,和UI组件彻底解耦。
而且在使用AndroidStudio创建Fragment的时候提供了创建ViewModel选项,极大的方便了开发。

参考
上车
佛系原创号主
本文同步分享在 博客“_龙衣”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
