Android Jetpack 库架构组件 ViewModel+LiveData 基础使用

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传参获取数据库的数据展示

  1. 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:
initDatainitView 执行顺序如下:
在这里插入图片描述

详细使用代码请参见:YGragon/FrameDemo

总结

使用ViewModel+LiveData的模式,可以很好的在组件的不同生命周期处理数据。这是由于LiveData是生命周期感知型类所带来的特性。ViewModel中专注于处理数据,和UI组件彻底解耦。

而且在使用AndroidStudio创建Fragment的时候提供了创建ViewModel选项,极大的方便了开发。
在这里插入图片描述

参考

上车

佛系原创号主
在这里插入图片描述

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

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

前端面试题自检 Vue 网络 浏览器 性能优化部分

框架VueMVVM是什么?ModelViewViewModel,Model表示数据模型层。view表示视图层,ViewModel是View和Model层的桥梁,数据绑定到viewModel层并自动渲染到页面中,视图变化通知viewModel层更新数据。Vue的生命周期

Android Jetpack 库架构组件 ViewModel+LiveData 基础使用 - HelloWorld