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
def lifecycle_version = "2.2.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// 还有一些可选项,可查看上面的版本依赖链接
创建
Viewmodel
(推荐创建BaseViewModel,之后再创建具体的ViewModel
继承BaseViewModel
)/**
- ViewModel 基类
*/ open class BaseViewModel : ViewModel() { override fun onCleared() { super.onCleared() Log.e("CommonViewModel","onCleared") } }
(示例1:倒计时功能)
class MineViewModel : BaseViewModel() { private val periodTime = 1000L private val mElapsedRealTime = MutableLiveData
() private var mInitialTime: Long = 0 // Create a LiveData with a Long val countDownTime: MutableLiveData<Long> by lazy { mElapsedRealTime } /** * 开启倒计时 */ fun getTime(){ mInitialTime = SystemClock.elapsedRealtime() val timer = Timer() val timeTask = object : TimerTask() { override fun run() { val newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000 if (newValue < 10){ mElapsedRealTime.postValue(newValue) }else{ timer.cancel() } } } timer.scheduleAtFixedRate(timeTask,periodTime,periodTime) }
}
(示例2:ViewModel传参获取数据库的数据展示)
class HomeViewModel(context: Context): BaseViewModel() { companion object { private const val PAGE_SIZE = 15 private const val ENABLE_PLACEHOLDERS = false } val mContext = context val dao = StudentDb.get(mContext).studentDao()
val allStudents = LivePagedListBuilder(dao.getAllStudent(), PagedList.Config.Builder() .setPageSize(PAGE_SIZE) //配置分页加载的数量 .setEnablePlaceholders(ENABLE_PLACEHOLDERS) //配置是否启动PlaceHolders .setInitialLoadSizeHint(PAGE_SIZE) //初始化加载的数量 .build()).build()
}
创建
Activity
(推荐Fragment
)中使用(示例1:倒计时功能) class MineFragment : BaseFragment(),MineContract.View {
private lateinit var viewModel: MineViewModel override fun initData() { // 创建实例 viewModel = ViewModelProviders.of(this).get(MineViewModel::class.java) // 观察数据 viewModel.countDownTime.observe(this, Observer<Long> { aLong -> //Update UI tv_name.text = "time = " + aLong!! }) } override fun initView() { // 订阅事件 lifecycle.addObserver(minePresenter) // 获取数据 btn_get_data.setOnClickListener { viewModel.getTime() } } // 省略部分代码,具体可看下方源码链接
}
(示例2:ViewModel传参获取数据库的数据展示) class HomeJetpackFragment : BaseFragment() { private val TAG = HomeJetpackFragment::class.java.simpleName private val viewModel by lazy(LazyThreadSafetyMode.NONE) { ViewModelProviders.of(this, object : ViewModelProvider.Factory { // 传递 context override fun <T : ViewModel?> create(modelClass: Class
): T = HomeViewModel( BaseApplication.context as Application ) as T }).get(HomeViewModel::class.java) } override fun initView() { val adapter = StudentAdapter() val layoutManager = LinearLayoutManager(activity) rv_list.layoutManager = layoutManager rv_list.adapter = adapter // 将数据的变化反映到UI上 viewModel.allStudents.observe(this, Observer { adapter.submitList(it) }) } // 省略部分代码,具体可看下方源码链接 }
Tip:initData
和 initView
执行顺序如下:
详细使用代码请参见:YGragon/FrameDemo
总结
使用ViewModel+LiveData
的模式,可以很好的在组件的不同生命周期处理数据。这是由于LiveData
是生命周期感知型类所带来的特性。ViewModel
中专注于处理数据,和UI
组件彻底解耦。
而且在使用AndroidStudio
创建Fragment
的时候提供了创建ViewModel
选项,极大的方便了开发。
参考
上车
佛系原创号主
本文同步分享在 博客“_龙衣”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。