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

Stella981
• 阅读 598

ViewModel 是什么

ViewModel 类旨在以注重生命周期的方式 存储和管理 界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。

为什么ViewModel 类中的数据可在发生屏幕旋转等配置更改后继续留存?
因为ViewModel的生命周期长于组件(Activity/Fragment)的生命周期

下图是左侧给出了Activity 经历屏幕旋转而后结束的过程,所处的各种生命周期状态。而右侧是ViewModel的生命周期。
从中可以看出ViewModel的生命周期是长于组件(Activity)的生命周期。
Android Jetpack 库架构组件 ViewModel+LiveData 基础使用
屏幕旋转后数据得以保存仅是ViewModel的其中一个优势,生命周期比组件长的优势还可以:

  • 减少资源浪费:避免因配置更改而重新创建对象
  • 避免内存泄露:界面控制器经常需要做异步调用,而异步调用需要在一段时间后才返回结果。如果界面关闭之后数据在还没返回,且其中的一些引用还存在,那么内存泄露就不可避免了。通常我们会在页面关闭的时候手动清除引用。在生命周期中我们可以看到ViewModel还有一个方法onCleared(),我们可在该方法手动清除引用。
    Android Jetpack 库架构组件 ViewModel+LiveData 基础使用
    谷歌在生命感知组件的最佳做法中,推荐我们使用ViewModel+LiveData的组合。
    Android Jetpack 库架构组件 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

    def lifecycle_version = "2.2.0"

    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    // 还有一些可选项,可查看上面的版本依赖链接
  1. 创建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()
    

    }

  2. 创建 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:
initDatainitView 执行顺序如下:
Android Jetpack 库架构组件 ViewModel+LiveData 基础使用

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

总结

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

而且在使用AndroidStudio创建Fragment的时候提供了创建ViewModel选项,极大的方便了开发。
Android Jetpack 库架构组件 ViewModel+LiveData 基础使用

参考

上车

佛系原创号主
Android Jetpack 库架构组件 ViewModel+LiveData 基础使用

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

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
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
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
劳伦斯 劳伦斯
3年前
前端面试题自检 Vue 网络 浏览器 性能优化部分
框架VueMVVM是什么?ModelViewViewModel,Model表示数据模型层。view表示视图层,ViewModel是View和Model层的桥梁,数据绑定到viewModel层并自动渲染到页面中,视图变化通知viewModel层更新数据。Vue的生命周期
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Stella981 Stella981
2年前
JetPack之ViewModel最新源码详细分析
本文会基于最新版ViewModel使用方法与源码进行详细分析,从注册到实现ViewModel界面数据如何保存与管理全部涉及。\\简介:\\ViewModel是JetPack系列库之一,它用来对组件的界面数据进行管理,且当组件的状态发生改变时数据依然留存。优点:1.当所依赖组件的状态发生改变时,例如屏幕旋转等,界面数据不会发
Wesley13 Wesley13
2年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这