Jetpack:在数据变化时如何优雅更新Views数据

news2025/1/11 5:23:05

在这里插入图片描述

本文讲的是关于Jetpack的架构组件LiveData,LiveData是Lifecycle-aware 组件的一个应用,这意味着LiveData遵守Activity、Fragment和Service等组件的生命周期,在它们生命周期处于活跃状态(CREATEDRESUMED)才进行更新Views。

使用LiveData步骤

  1. 创建持有某种类型的LiveData对象。通常在ViewModel类来实现该对象。
  2. 定义一个具有onChanged()方法的Observer对象,当LiveData持有数据变化是回调该方法。通常在UI控制器类中实现创建该Observer对象,如Activity或Fragment。
  3. 通过使用observe()方法,将上述的LiveData对象和Observer对象关联在一起。这样Observer对象就与LiveData产生了订阅关系,当LiveData数据发生变化时通知,而在Observer更新数据,所以Observer通常是Activity和Fragment。

三个步骤就定义了使用LiveData的方式,从步骤可以看出,使用了观察者模式,当LiveData对象持有数据发生变化,会通知对它订阅的所有处于活跃状态的订阅者。而这些订阅者通常是UI控制器,如Activity或Fragment,以能在被通知时,自动去更新Views。

创建LiveData对象

LiveData可以包装任何数据,包括集合对象。LiveData通常存储在ViewModel中,并通过getter方法获得。示例:

class NameViewModel : ViewModel() {

    // Create a LiveData with a String
    val currentName: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }

    // Rest of the ViewModel...
}

为什么是ViewModel持有LiveData而不是Activity或者Fragment中呢?

  • 这样导致Activity或Fragment代码臃肿,Activity或Fragment一般用来展示数据而不是持有数据。
  • 将LiveData解耦,不和特定的Activity或Fragment绑定在一起。

创建 观察LiveData 的对象

有了数据源之后,总需要有观察者来观察数据源,不然数据源就失去了存在的意义。

那么在哪里观察数据源呢?

在大多数情况下,在应用组件的onCreate()方法中访问LiveData是个合适的时机。这样可以确保系统不在Activity或Fragment的onResume()方法进行多余的调用;另外这样也确保Activity和Fragment尽早有数据可以进行显示。

class NameActivity : AppCompatActivity() {

    private lateinit var model: NameViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Other code to setup the activity...

        // Get the ViewModel.
        model = ViewModelProviders.of(this).get(NameViewModel::class.java)

        // Create the observer which updates the UI.
        val nameObserver = Observer<String> { newName ->
            // Update the UI, in this case, a TextView. 
            nameTextView.text = newName
        }

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.currentName.observe(this, nameObserver)
    }
}

在讲nameObserver对象传给observe()方法后,存储在LiveData最近的值以参数的形式立即传递到onChange()方法中。当然,如果此时LiveData没有存储值的话,onChange()方法不会被调用。

更新 LiveData 对象

LiveData本身没有提供公共方法更新值。如果需要修改LiveData的数据的话,可以通过MutableLiveData来暴露共有方法setValue()postValue()。通常在在ViewModel中使用MutableLiveData,而MutableLiveData暴露不可变的LiveData给Observer。与Observer建立关系后,通过修改LiveData的值从而更新Observer中的视图。

button.setOnClickListener {
    val anotherName = "GitCode"
    model.currentName.setValue(anotherName)
}

当单击button时,字符串GitCode会存储到LiveData中,nameTextView的文本也会更新为GitCode。这里通过button的点击来给LiveData设置值,也可以网络或者本地数据库获取数据方式来设置值。

扩展 LiveData

可以通过下面的栗子来看看如何扩展LiveData。

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        value = price
    }

    override fun onActive() {
        stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
        stockManager.removeUpdates(listener)
    }
}

首先建立一个StockLiveData并继承自LiveData,并重写两个重要方法。

  • onActivite() 当有活跃状态的订阅者订阅LiveData时会回调该方法。意味着需要在这里监听数据的变化。
  • onInactive() 当没有活跃状态的订阅者订阅LiveData时会回调该方法。此时没有必要保持StockManage服务象的连接。
  • setValue() 注意到value=price这里是调用了setValue(price)方法,通过该方法更新LiveData的值,进而通知处于活跃状态的订阅者。

LiveData会认为订阅者的生命周期处于STARTEDRESUMED状态时,该订阅者是活跃的。

那么如何使用StockLiveData呢?

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    val myPriceListener: LiveData<BigDecimal> = ...
    myPriceListener.observe(this, Observer<BigDecimal> { price: BigDecimal? ->
        // Update the UI.
    })
}

以Fragment作LifecycleOwner的实例传递到observer()方法中,这样就将Observer绑定到拥有生命周期的拥有者。由于LiveData可以在多个Activity、Fragment和Service中使用,所以可以创建单例模式。

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager: StockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        value = price
    }

    override fun onActive() {
        stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
        stockManager.removeUpdates(listener)
    }

    companion object {
        private lateinit var sInstance: StockLiveData

        @MainThread
        fun get(symbol: String): StockLiveData {
            sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
            return sInstance
        }
    }
}

那么在Fragment可以这样使用:

class MyFragment : Fragment() {

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        StockLiveData.get(symbol).observe(this, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })

    }

转换 LiveData

有时候在把数据分发给Observer前,转换存储在LiveData中的值,或者返回一个 基于已有值的LiveData对象 的另外一个LiveData对象。这时候就需要用到 Transformations类来处理了。

使用Transformations.map()方法可以改变其下游的结果:

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});

使用Transformations.switchMap()同样可以改变下游的结果,但传递给switchMap()的函数必须返回一个LiveData对象。

private fun getUser(id: String): LiveData<User> {
  ...
}
val userId: LiveData<String> = ...
val user = Transformations.switchMap(userId) { id -> getUser(id) }

这种转换方式都惰性的,也就是只有Observer来订阅数据的时候,才会进行转换。当在ViewModel需要一个Lifecycle对象,或许这种转换会是很好的解决方案。

合并多个LiveData 源

MediatorLiveData是LiveData的子类,它主要用途是用来合并多个LiveData源。当其中一个源数据发生变化是,都会回调订阅MediatorLiveData的观察者的onChanged()方法。例如我们在实际开发中,我们的数据源要么来自服务器,要么来自本地数据库。这里就考虑使用MediatorLiveData。

总结

LiveData的入门使用来说还是相对简单的,等到后面讲完Jetpack系列文章,再以一个综合的Demo示例Jetpack涉及到的一些知识点。光看文档,都可以感觉到Android 对设计模式,和MVP模式、MVVM模式设计理念使用得淋漓尽致。所以建议各位同学在代码方面的编写一定要有大局观念,代码规范的还是要有,方便别人就是方便自己。不要为应付功能实现而代码臃肿,后续又不重新架构,一直积累成垃圾码。

最后

如果你看到了这里,觉得文章写得不错就给个赞呗?

更多Android进阶指南 可以扫码 解锁 《Android十大板块文档》

1.Android车载应用开发系统学习指南(附项目实战)

2.Android Framework学习指南,助力成为系统级开发高手

3.2023最新Android中高级面试题汇总+解析,告别零offer

4.企业级Android音视频开发学习路线+项目实战(附源码)

5.Android Jetpack从入门到精通,构建高质量UI界面

6.Flutter技术解析与实战,跨平台首要之选

7.Kotlin从入门到实战,全方面提升架构基础

8.高级Android插件化与组件化(含实战教程和源码)

9.Android 性能优化实战+360°全方面性能调优

10.Android零基础入门到精通,高手进阶之路

敲代码不易,关注一下吧。ღ( ´・ᴗ・` ) 🤔

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1030614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《计算机视觉中的多视图几何》笔记(8)

8 More Single View Geometry 本章主要讲述除了点以外的几何体&#xff0c;在投影变换下的性质。这些几何体包括&#xff1a;平面&#xff0c;线&#xff0c;圆锥曲线&#xff0c;二次曲线。 讲到这里就明白了&#xff0c;为什么投影几何这么重要&#xff0c;因为摄像机就是一…

VLANIF配置

目录 实验原理&#xff1a; 案例&#xff1a; 设备配置 用ping验证不同vlan之间实现相互通信 实验原理&#xff1a; VLANIF接口是一种第三层的逻辑接口&#xff0c;用于在第三层实现不同VLAN 之间的通信。 每个VALN有一个VLANIF接口&#xff0c;并通过该接口在网络层转发…

【操作系统笔记】缓存一致性

CPU 核心之间数据如何传播 高速缓存中的值被修改了&#xff0c;那么怎么同步到内存中呢&#xff1f; ① 写直达&#xff08;Write-Through&#xff09;② 写回&#xff08;Write-Back&#xff09; 写直达&#xff08;Write-Through&#xff09; 简单&#xff0c;但是很慢&am…

《Kubernetes部署篇:Ubuntu20.04基于containerd二进制部署K8S 1.25.14集群(多主多从)》

一、架构图 如下图所示&#xff1a; 二、部署说明 2.1、部署流程 1、系统环境初始化&#xff0c;主要包括 主机名设置、主机hosts解析、关闭防火墙、关闭swap分区、修改系统参数、时间时区同步、修改内核参数、启用ipvs模式。 2、使用一键生成K8S集群证书工具创建证书文件。…

vue页面嵌入飞书网页组件,用于在类似ERP,OA等系统中展示在线文档

先展示最终效果(就是在vue页面中,内嵌了一块ifream页面): 1. 注册进入飞书开放平台,地址为: 飞书开放平台 2.进入开放平台后,选择--创建企业自建应用--创建网页应用,然后在主页面记住该应用的appId和appSecret参数,后面要用 3.然后注意一点的是,因为后面的授权等逻辑我们一般…

Android设计支持库

本文所有的代码均存于 https://github.com/MADMAX110/BitsandPizzas 设计支持库&#xff08;Design Support Library&#xff09;是 Google 在 2015 年的 I/O 大会上发布的全新 Material Design 支持库&#xff0c;在这个 support 库里面主要包含了 8 个新的 Material Design …

clickhouse简单安装部署

目录 前言(来源于官方文档)&#xff1a; 一.下载并上传 1.下载地址&#xff1a;点我跳转下载 2.上传至Linux 二.解压和配置 1.解压顺序 注意&#xff1a;必须按照以下顺序解压&#xff0c;并且每解压一个都要执行该解压后文件的install/doinst.sh文件 解压步骤&#xff…

antd-design-vue Table组件全局配置(分页器...)

描述&#xff1a;该框架许多默认配置好像还不支持&#xff0c;一般都是挨个使用挨个配置。我的项目中也遇到了类似的情况&#xff0c;但是当需求发生变化时&#xff0c;代码所有的组件使用则都需要修改&#xff0c;这种方式真的很不礼貌。 《我为了一口醋包了顿饺子》 需求是将…

MQ - 19 安全_限流方案的设计

文章目录 导图Pre概述集群中的数据加密加密算法分类消息队列限流机制思考单机限流全局限流全局限流还是单机限流?对哪些资源和维度进行限流发生限流后怎么处理消息队列全局限流设计单机限流方案全局限流方案消息队列的服务降级配置 Broker 的 CPU 或内存的使用率额度配置磁盘保…

C语言每日一题(5):求两个数二进制中不同位的个数

文章主题&#xff1a;求两个数二进制中不同位的个数&#x1f525;所属专栏&#xff1a;C语言每日一题&#x1f4d7;作者简介&#xff1a;每天不定时更新C语言的小白一枚&#xff0c;记录分享自己每天的所思所想&#x1f604;&#x1f3b6;个人主页&#xff1a;[₽]的个人主页&a…

BOA服务器移植

BOA服务器移植 1、源码下载 http://www.boa.org/ News! (last updated 23 February 2005) Latest Released Version (0.94.13) here (signature here) --- 下载地址1.1 boa简介&#xff1a; 其可执行代码只有大约60KB左右&#xff0c;Boa是一个单任务的HTTP服务器&#xff…

MyBatisPlus(五)配置表名前缀:table-prefix

说明 数据库中的表名&#xff0c;和项目中的实体类名&#xff0c;并不符合默认对应规则。所有表都有同样的前缀&#xff0c;而实体类名是和表名去掉前缀后的部分对应的。 解决方案 配置全局表名前缀&#xff0c;使实体类名对应的表名&#xff0c;自动加上一段前缀。 限制 …

JSON 串和 Java 对象的相互转换

JSON 串和 Java 对象的相互转换 以 json 格式的数据进行前后端交互 前端发送请求时&#xff0c;如果是复杂的数据就会以 json 提交给后端&#xff1b; 而后端如果需要响应一些复杂的数据时&#xff0c;也需要以 json 格式将数据响应回给浏览器 为达到以上目的就需要重点学习…

基于微信小程序的美术馆预约平台设计与实现(源码+lw+部署文档+讲解等)

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…

Linux_9_网络协议和管理

目录 1网络基础1.1 网络概念1.2常见的网络物理组件1.3网络应用程序1.3.1各种网络应用1.3.2应用程序对网络的要求 1.4网络的特征1.4.1速度(带宽)1.4.2网络拓扑 1.5网络1.5.1网络准和分层1.5.2开放系统互联OSI1.5.3网络的通信过程1.5.3.1数据封装和数据解封1.5.3.2协议数据单元PD…

基于SSM的四六级报名与成绩查询系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

机器学习 day36(纯度)

熵 这些例子的纯度和熵如图所示&#xff0c;且左侧为熵函数图熵函数是判断某组数据是否纯度高的指标 熵函数公式如上图&#xff0c;底数为2仅为了使函数峰值为1&#xff0c;且设定0log(0)为0&#xff0c;但log(0)为无穷大

【Java 基础篇】Java实现文件搜索详解

文件搜索是计算机应用中的一个常见任务&#xff0c;它允许用户查找特定文件或目录&#xff0c;以便更轻松地管理文件系统中的内容。在Java中&#xff0c;您可以使用各种方法来实现文件搜索。本文将详细介绍如何使用Java编写文件搜索功能&#xff0c;以及一些相关的内容。 文件…

【数据结构】图的应用:最小生成树;最短路径;有向无环图描述表达式;拓扑排序;逆拓扑排序;关键路径

目录 1、最小生成树 1.1 概念 1.2 普利姆算法&#xff08;Prim&#xff09; 1.3 克鲁斯卡尔算法&#xff08;Kruskal&#xff09; 2、最短路径 2.1 迪杰斯特拉算法&#xff08;Dijkstra&#xff09; 2.2 弗洛伊德算法&#xff08;Floyd&#xff09; 2.3 BFS算法&…

windows主机和vmware ubuntu18.04虚拟机ping通

windows主机 网线连接电脑&#xff0c;读取当前windows的相关信息 powershell 更改IP地址指令 New-NetIPAddress -InterfaceIndex 23 -IPAddress 192.168.0.105 -PrefixLength 24 -DefaultGateway 192.168.0.1 虚拟机 虚拟机需要管理员权限打开 选择桥接方式 IPV4地址改成同…