Jetpack系列:LiveData入门级使用方法

Android APP开发中,开发者们都想有一个公共的组件,可以实现后台数据的监听,同时实时更新到UI进行显示,从而大大简化开发过程。Google针对这一开发需求,提供了Jetpack LiveData组件。下面我们来一起看下LiveData的基本使用方法吧!

首先,先了解下使用LiveData的优点。

  • 确保UI与数据状态匹配
  • 不需要担心内存泄漏问题
  • Activity停止后数据变化不会导致Crash
  • 不再需要人工生命周期的处理
  • 始终使用最新的数据
  • 正确应用配置更改
  • 共享资源

LiveData遵循观察者模式,实现LifeCycle接口,因此可以监听数据的实时更新,感知应用的生命周期,让开发者能够更多的关注业务具体实现。

下面我们来通过一个小Demo来简单介绍下LiveData的基本使用方法。

本例中,数据变化通知UI的显示由四个控件体现,分别为:系统时间(Long型)、系统时间、天气、远端数据。针对这四个控件的动态显示,我们分别来看下其是如何实现的。

框架搭建

APP首先需要搭建使用LiveData的环境:

1. 导入依赖包

//app build.gradle
dependencies {
    ...
    implementation deps.lifecycle.viewmodel_ktx
    implementation deps.lifecycle.livedata_ktx
    ...
}

2. 创建ViewModel类(用于LiveData数据的封装,和UI交互)

class LiveDataViewModel(
    private val dataSource: DataSource
) : ViewModel() {...}

3. 布局文件中引用ViewModel对象

 <layout>
     <data>
         <variable
             name="viewmodel"
             type="com.android.example.livedatabuilder.LiveDataViewModel" />
     </data>
     ...
</layout>

4. Activity绑定ViewModel

//MainActivity
//成员变量
private val viewmodel: LiveDataViewModel by viewModels { LiveDataVMFactory }
//onCreate
val binding = DataBindingUtil.setContentView<ActivityLivedataBinding>(
            this, R.layout.activity_livedata
        )
// Set the LifecycleOwner to be able to observe LiveData objects
binding.lifecycleOwner = this

// Bind ViewModel
binding.viewmodel = viewmodel
//LifeDataVMFactory
object LiveDataVMFactory : ViewModelProvider.Factory {
    private val dataSource = DefaultDataSource(Dispatchers.IO)
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        @Suppress("UNCHECKED_CAST")
        return LiveDataViewModel(dataSource) as T
    }
}

注意:此处构造ViewModel采用的dataSource为DefaultDataSource,后续数据是根据此数据源来进行获取的。

系统时间(Long型)显示

系统时间的显示,通过在UI上绑定ViewModel,通过getCurrentTime方法后台更新、提交数据,来通知UI进行显示的更新。

//xml
<TextView
    android:id="@+id/time"
    android:text="@{Long.toString(viewmodel.currentTime)}"
    .../>
//LiveDataViewModel
val currentTime = dataSource.getCurrentTime()
//DefaultDataSource
override fun getCurrentTime(): LiveData<Long> =
        liveData {
            while (true) {
                emit(System.currentTimeMillis())//通知当前系统时间
                delay(1000)//延时1秒
            }
        }

系统时间显示

系统时间的显示是根据系统获取的Long型变量变化映射得到的,Long值发生变化时,实时更新系统时间显示。

//xml
<TextView
    android:id="@+id/time_transformed"
    android:text="@{viewmodel.currentTimeTransformed}"
    .../>
//LiveDataViewModel 此处有两种方式实现
//1. currentTime变更后实时通知UI更新
val currentTimeTransformed : LiveData<String> = Transformations.map(currentTime) {
        Date(it).toString()
    }
//2. 延时500ms后通知
val currentTimeTransformed = currentTime.switchMap {
    // timeStampToTime is a suspend function so we need to call it from a coroutine.
    liveData { emit(timeStampToTime(it)) }
}
private suspend fun timeStampToTime(timestamp: Long): String {
    delay(500)  // Simulate long operation
    val date = Date(timestamp)
    return date.toString()
}

天气显示

天气的显示通过动态改变数据源提供的数据,从而通知UI显示(DataSource数据的更新实时通过LiveData传递到UI)。

//xml
<TextView
    android:id="@+id/current_weather"
    android:text="@{viewmodel.currentWeather}"
    .../>
//LiveDataViewModel
val currentWeather: LiveData<String> = liveData {
    emit(LOADING_STRING)
    emitSource(dataSource.fetchWeather())
}
//DefaultDataSource
private val weatherConditions = listOf("Sunny", "Cloudy", "Rainy", "Stormy", "Snowy")
override fun fetchWeather(): LiveData<String> = liveData {
    var counter = 0
    while (true) {
        counter++
        delay(2000)//延时两秒
        //按顺序循环显示weatherConditions中的天气数据信息
        emit(weatherConditions[counter % weatherConditions.size])
    }
}

远端数据显示

远端数据的请求通过Button的点击事件触发,数据获取成功后,通知TextView进行数据显示。

//xml
<TextView
    android:id="@+id/cached_value"
    android:text="@{viewmodel.cachedValue}"
    .../>
<Button
    android:id="@+id/refresh_button"
    android:onClick="@{() -> viewmodel.onRefresh()}"
    .../>
//LiveDataViewModel
val cachedValue = dataSource.cachedData
fun onRefresh() {
    // Launch a coroutine that reads from a remote data source and updates cache
    viewModelScope.launch {
        dataSource.fetchNewData()
    }
}
//DefaultDataSource
private val _cachedData = MutableLiveData("This is old data")
override val cachedData: LiveData<String> = _cachedData
override suspend fun fetchNewData() {
    // Force Main thread
    withContext(Dispatchers.Main) {
        _cachedData.value = "Fetching new data..."
        _cachedData.value = simulateNetworkDataFetch()
    }
}
private var counter = 0
// Using ioDispatcher because the function simulates a long and expensive operation.
private suspend fun simulateNetworkDataFetch(): String = withContext(ioDispatcher) {
    delay(3000)//延时3秒
    counter++
    "New data from request #$counter"//返回此字符串
}
小提示:本例中的viewModelScope使用的是Kotlin Coroutines(协程)功能,更多协程使用方法,请查看Coroutines在架构组件中的应用:官方文档链接

远端数据的更新流程为:

将上述四个控件分别绑定对应的LiveData对象,增加其数据变化,就能够实现前文描述的APP动态变化效果了。

帮助文档
?
源码路径

小技巧: github 代码下载速度慢,可以克隆到码云上(gitee.com)再下载。

通过这四个控件的LiveData与UI的交互使用,你学会如何使用LiveData了吗?

欢迎关注公众号,留言讨论更多技术问题。

原文地址:https://www.cnblogs.com/danvie/p/11612370.html

时间: 2024-08-29 16:33:10

Jetpack系列:LiveData入门级使用方法的相关文章

μC/OS-II事件标志组的入门级使用方法

试想如下情况,有A.B.C三个事件,当A.B都满足某一条件(或执行某一动作)后C才能得到运行(持续运行或运行一次后继续等待A.B条件再次满足后再运行). 如果需要实现这样的功能,就可以用事件标志组来实现了! 具体看实例: //定义一个事件标志 OS_FLAG_GRP *Sem_Flg = 0; //LED0任务 void led0_task(void *pdata) { INT8U  err = 0; pdata = pdata; //创建一个事件标志 Sem_Flg = OSFlagCreat

Angular系列----AngularJS入门教程04:迭代器过滤(转载)

我们在上一步做了很多基础性的训练,所以现在我们可以来做一些简单的事情喽.我们要加入全文检索功能(没错,这个真的非常简单!).同时,我们也会写一个端到端测试,因为一个好的端到端测试可以帮上很大忙.它监视着你的应用,并且在发生回归的时候迅速报告. 请重置工作目录: git checkout -f step-3 我们的应用现在有了一个搜索框.注意到页面上的手机列表随着用户在搜索框中的输入而变化. 步骤2和步骤3之间最重要的不同在下面列出.你可以在GitHub里看到完整的差别. 控制器 我们对控制器不做

Angular系列---- AngularJS入门教程03:AngularJS 模板(转载)

是时候给这些网页来点动态特性了——用AngularJS!我们这里为后面要加入的控制器添加了一个测试. 一个应用的代码架构有很多种.对于AngularJS应用,我们鼓励使用模型-视图-控制器(MVC)模式解耦代码和分离关注点.考虑到这一点,我们用AngularJS来为我们的应用添加一些模型.视图和控制器. 请重置工作目录: git checkout -f step-2 我们的应用现在有了一个包含三部手机的列表. 步骤1和步骤2之间最重要的不同在下面列出.,你可以到GitHub去看完整的差别. 视图

Angular系列----AngularJS入门教程01:AngularJS模板 (转载)

是时候给这些网页来点动态特性了——用AngularJS!我们这里为后面要加入的控制器添加了一个测试. 一个应用的代码架构有很多种.对于AngularJS应用,我们鼓励使用模型-视图-控制器(MVC)模式解耦代码和分离关注点.考虑到这一点,我们用AngularJS来为我们的应用添加一些模型.视图和控制器. 请重置工作目录: git checkout -f step-2 我们的应用现在有了一个包含三部手机的列表. 步骤1和步骤2之间最重要的不同在下面列出.,你可以到GitHub去看完整的差别. 视图

Angular系列------AngularJS入门教程:导言和准备(转载)

学习AngularJS的一个好方法是逐步完成本教程,它将引导您构建一个完整的AngularJS web应用程序. 该web应用是一个Android设备清单的目录列表,您可以筛选列表以便查看您感兴趣的设备,然后查看设备的详细信息. 本教程将向您展示AngularJS怎样使得web应用更智能更灵活,而且不需要各种扩展程序或插件. 通过本教程的学习,您将: 阅读示例学习怎样使用AngularJS的客户端数据绑定和依赖注入功能来建立可立即响应用户操作的动态数据视图. 学习如何使用AngularJS创建数

Angular系列----AngularJS入门教程05:双向绑定(转载)

在这一步你会增加一个让用户控制手机列表显示顺序的特性.动态排序可以这样实现,添加一个新的模型属性,把它和迭代器集成起来,然后让数据绑定完成剩下的事情. 请重置工作目录: git checkout -f step-4 你应该发现除了搜索框之外,你的应用多了一个下来菜单,它可以允许控制电话排列的顺序. 步骤3和步骤4之间最重要的不同在下面列出.你可以在GitHub里看到完整的差别. 模板 app/index.html Search: <input ng-model="query"&g

C# 串口操作系列(2) -- 入门篇,为什么我的串口程序在关闭串口时候会死锁 ?

C# 串口操作系列(2) -- 入门篇,为什么我的串口程序在关闭串口时候会死锁 ? 标签: c#objectuibyte通讯.net 2010-05-19 08:43 55212人阅读 评论(188) 收藏 举报  分类: 通讯类库设计(4)  版权声明:本文为博主原创文章,未经博主允许不得转载. 第一篇文章我相信很多人不看都能做的出来,但是,用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁.天哪,我可不是武断,算了.不要太绝对了.99.9%的人吧,都遇到过这个问

Jetpack学习-LiveData

个人博客 http://www.milovetingting.cn Jetpack学习-LiveData LiveData是什么 LiveData是一种可观察的数据存储器类,具有生命周期的感知能力. 简单使用 LiveData一般都是和ViewModel一起使用.定义一个类继承自ViewModel: public class LiveDataSub extends ViewModel { private MutableLiveData<String> infos; private int nu

软件测试系列之入门篇

一.你知道软件测试有多重要吗? 在国际上,软件测试(软件质量控制)是一件非常重要的工程工作,测试也作为一个非常独立的职业.在IBM.Microsoft等开发大型系统软件公司,很多重要项目的开发测试人员的比例能够达到1:2甚至1:4. 在国内软件测试的地位还不够高,并且大多只停留在软件单元测试.集成测试和功能测试上.软件测试从业人员的数量同实际需求有不小差距,国内软件企业中开发人员与测试人员数量一般为5:1,因此,国内的软件测试产业化还有待开发和深掘. 说到这里不知道你反应是高兴还是失望?但是我却