当前位置: 首页 > article >正文

Jetpack LiveData 使用与原理解析

一、引言

在 Android 开发中,数据的变化需要及时反映到界面上是一个常见的需求。然而,传统的方式可能会导致代码复杂、难以维护,并且容易出现内存泄漏等问题。Jetpack 组件中的 LiveData 为我们提供了一种优雅的解决方案,它是一种可观察的数据持有者类,具有生命周期感知能力,能够确保数据更新时只在合适的生命周期状态下通知观察者,从而避免了内存泄漏和空指针异常等问题。本文将详细介绍 LiveData 的使用方法,并深入剖析其源码原理。

二、LiveData 基本使用

2.1 添加依赖

要使用 LiveData,需要在项目的 build.gradle 文件中添加以下依赖:

implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'

2.2 创建 LiveData 对象

LiveData 是一个抽象类,通常使用它的子类 MutableLiveData 来创建可修改的 LiveData 对象。以下是一个简单的示例:

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModelclass MyViewModel : ViewModel() {// 创建一个 MutableLiveData 对象,用于存储整数类型的数据val currentNumber: MutableLiveData<Int> by lazy {MutableLiveData<Int>().also {it.value = 0}}// 增加数字的方法fun incrementNumber() {currentNumber.value = (currentNumber.value ?: 0) + 1}
}

在这个示例中,我们创建了一个 MyViewModel 类,其中包含一个 MutableLiveData 类型的 currentNumber 对象,并提供了一个方法 incrementNumber 来更新该对象的值。

2.3 观察 LiveData 对象

在 Activity 或 Fragment 中,我们可以通过 observe 方法来观察 LiveData 对象的变化,并在数据更新时执行相应的操作。以下是在 Activity 中观察 currentNumber 的示例:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import com.example.livedatademo.databinding.ActivityMainBindingclass MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingprivate lateinit var viewModel: MyViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)// 获取 ViewModel 实例viewModel = ViewModelProvider(this).get(MyViewModel::class.java)// 观察 LiveData 对象viewModel.currentNumber.observe(this) { newNumber ->// 当数据更新时,更新界面显示binding.numberTextView.text = newNumber.toString()}// 设置按钮点击事件,调用 ViewModel 中的方法更新数据binding.incrementButton.setOnClickListener {viewModel.incrementNumber()}}
}

MainActivity 中,我们通过 observe 方法观察 currentNumber 的变化,当 currentNumber 的值发生改变时,会触发 lambda 表达式中的代码,更新界面上的文本显示。

2.4 其他使用场景

转换 LiveData

LiveData 提供了一些转换方法,如 mapswitchMap,用于对 LiveData 中的数据进行转换。以下是使用 map 方法的示例:

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModelclass MyViewModel : ViewModel() {private val _inputData = MutableLiveData<String>()val outputData: LiveData<String> = Transformations.map(_inputData) { input ->// 对输入数据进行转换"Transformed: $input"}fun setInputData(input: String) {_inputData.value = input}
}

在这个示例中,我们使用 Transformations.map 方法将 _inputData 中的数据进行转换,并将转换后的结果存储在 outputData 中。

合并 LiveData

可以使用 MediatorLiveData 来合并多个 LiveData 对象,当其中任何一个 LiveData 对象的数据发生变化时,MediatorLiveData 都会收到通知。以下是一个简单的示例:

import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModelclass MyViewModel : ViewModel() {private val _source1 = MutableLiveData<String>()private val _source2 = MutableLiveData<String>()val mergedData: MediatorLiveData<String> = MediatorLiveData<String>().apply {// 添加第一个数据源addSource(_source1) { value ->value?.let {this.value = "Source 1: $it"}}// 添加第二个数据源addSource(_source2) { value ->value?.let {this.value = "Source 2: $it"}}}fun setSource1Data(data: String) {_source1.value = data}fun setSource2Data(data: String) {_source2.value = data}
}

在这个示例中,MediatorLiveData 合并了 _source1_source2 两个 LiveData 对象,当其中任何一个数据源的数据发生变化时,mergedData 都会更新。

三、LiveData 源码原理解析

3.1 LiveData 核心类结构

LiveData 主要涉及以下几个核心类:

  • LiveData:抽象类,定义了 LiveData 的基本行为和接口。
  • MutableLiveDataLiveData 的子类,提供了 setValuepostValue 方法用于更新数据。
  • Observer:观察者接口,用于定义数据更新时的回调方法。
  • LifecycleBoundObserver:实现了 Observer 接口,并且具有生命周期感知能力,确保只在合适的生命周期状态下通知观察者。

3.2 数据更新机制

setValue 方法

setValue 方法用于在主线程中更新 LiveData 的值,并通知所有活跃的观察者。以下是 setValue 方法的源码:

@MainThread
protected void setValue(T value) {assertMainThread("setValue");mVersion++;mData = value;dispatchingValue(null);
}

setValue 方法中,首先会检查当前线程是否为主线程,然后更新数据的版本号和数据值,最后调用 dispatchingValue 方法来通知观察者。

postValue 方法

postValue 方法用于在非主线程中更新 LiveData 的值,它会将更新操作发送到主线程中执行。以下是 postValue 方法的源码:

protected void postValue(T value) {boolean postTask;synchronized (mDataLock) {postTask = mPendingData == NOT_SET;mPendingData = value;}if (!postTask) {return;}ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}private final Runnable mPostValueRunnable = new Runnable() {@SuppressWarnings("unchecked")@Overridepublic void run() {Object newValue;synchronized (mDataLock) {newValue = mPendingData;mPendingData = NOT_SET;}setValue((T) newValue);}
};

postValue 方法中,会将新的值存储在 mPendingData 中,并通过 ArchTaskExecutormPostValueRunnable 发送到主线程中执行。在 mPostValueRunnable 中,会调用 setValue 方法来更新数据并通知观察者。

3.3 观察者注册与通知机制

observe 方法

observe 方法用于注册一个具有生命周期感知能力的观察者。以下是 observe 方法的源码:

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {assertMainThread("observe");if (owner.getLifecycle().getCurrentState() == DESTROYED) {// 如果 LifecycleOwner 已经销毁,直接返回return;}LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);if (existing != null && !existing.isAttachedTo(owner)) {throw new IllegalArgumentException("Cannot add the same observer"+ " with different lifecycles");}if (existing != null) {return;}owner.getLifecycle().addObserver(wrapper);
}

observe 方法中,首先会检查当前线程是否为主线程,然后检查 LifecycleOwner 的状态。如果 LifecycleOwner 已经销毁,则直接返回。接着会创建一个 LifecycleBoundObserver 对象,并将其存储在 mObservers 中。最后,将 LifecycleBoundObserver 注册到 LifecycleOwner 的生命周期中。

dispatchingValue 方法

dispatchingValue 方法用于通知观察者数据更新。以下是 dispatchingValue 方法的源码:

private void dispatchingValue(@Nullable ObserverWrapper initiator) {if (mDispatchingValue) {mDispatchInvalidated = true;return;}mDispatchingValue = true;do {mDispatchInvalidated = false;if (initiator != null) {considerNotify(initiator);initiator = null;} else {for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}}}} while (mDispatchInvalidated);mDispatchingValue = false;
}

dispatchingValue 方法中,会遍历所有的观察者,并调用 considerNotify 方法来通知它们数据更新。

considerNotify 方法

considerNotify 方法用于检查观察者的生命周期状态,并在合适的状态下通知观察者。以下是 considerNotify 方法的源码:

private void considerNotify(ObserverWrapper observer) {if (!observer.mActive) {return;}if (!observer.shouldBeActive()) {observer.activeStateChanged(false);return;}if (observer.mLastVersion >= mVersion) {return;}observer.mLastVersion = mVersion;// 调用观察者的 onChanged 方法通知数据更新observer.mObserver.onChanged((T) mData);
}

considerNotify 方法中,会检查观察者的活跃状态和版本号,如果观察者不活跃或者版本号已经是最新的,则不会通知观察者。否则,会调用观察者的 onChanged 方法通知数据更新。

四、总结

LiveData 是 Jetpack 组件中一个非常实用的工具,它通过生命周期感知能力和数据更新通知机制,为开发者提供了一种简洁、安全的方式来处理数据和界面的交互。通过 setValuepostValue 方法可以更新数据,通过 observe 方法可以注册观察者。在源码层面,LiveData 通过 LifecycleBoundObserver 实现了生命周期感知,确保只在合适的生命周期状态下通知观察者。合理使用 LiveData 可以提高代码的可维护性和稳定性,避免内存泄漏和空指针异常等问题。在实际开发中,结合 ViewModel 等其他 Jetpack 组件,可以构建出更加高效、健壮的 Android 应用。

相关文章:

Jetpack LiveData 使用与原理解析

一、引言 在 Android 开发中&#xff0c;数据的变化需要及时反映到界面上是一个常见的需求。然而&#xff0c;传统的方式可能会导致代码复杂、难以维护&#xff0c;并且容易出现内存泄漏等问题。Jetpack 组件中的 LiveData 为我们提供了一种优雅的解决方案&#xff0c;它是一种…...

HarmonyOS Next~鸿蒙系统开发类Kit深度解析与应用实践

HarmonyOS Next&#xff5e;鸿蒙系统开发类Kit深度解析与应用实践 鸿蒙系统&#xff08;HarmonyOS&#xff09;凭借其分布式架构和全场景能力&#xff0c;为开发者提供了丰富的开发类Kit。本文将从安全、网络、基础功能、硬件及调测五大类Kit展开&#xff0c;深入探讨其核心技…...

网盘解析工具更新,解决了一些bug

解析工具v1.2.1版本更新&#xff0c;本次是小版本更新&#xff0c;修复了一些bug。 之前小伙伴反应的网盘进入文件后不能返回上一级&#xff0c;现在这个bug修复了&#xff0c;已经可以点击了。 点击资源后会回到资源那一级目录&#xff0c;操作上是方便了不少。 增加了检查自…...

SQL语句及其应用(上) (DDL语句以及DML语句)

SQL语句的定义: 概述: 全称叫 Structured Query Language, 结构化查询语言, 主要是实现 用户(程序员) 和 数据库软件(例如: MySQL, Oracle)之间交互用的. 分类: DDL: 数据定义语言, 主要是操作 数据库, 数据表, 字段, 进行: 增删改查(CURD) 涉及到的关键字: create, drop, …...

混境之地1

问题描述 小蓝有一天误入了一个混境之地。 好消息是&#xff1a;他误打误撞拿到了一张地图&#xff0c;并从中获取到以下信息&#xff1a; 混境之地的大小为 n⋅mn⋅m&#xff0c;其中 # 表示这个位置很危险&#xff0c;无法通行&#xff0c;. 表示道路&#xff0c;可以通行。他…...

5种生成模型(VAE、GAN、AR、Flow 和 Diffusion)的对比梳理 + 易懂讲解 + 代码实现

目录 1 变分自编码器&#xff08;VAE&#xff09;​ 1.1 概念 1.2 训练损失 1.3 VAE 的实现 2 生成对抗网络&#xff08;GAN&#xff09;​ 2.1 概念 2.2 训练损失 a. 判别器的损失函数 b. 生成器的损失函数 c. 对抗训练的动态过程 2.3 GAN 的实现 3 自回归模型&am…...

doris:查询熔断

查询熔断是一种保护机制&#xff0c;用于防止长时间运行或消耗过多资源的查询对系统产生负面影响。当查询超过预设的资源或时间限制时&#xff0c;熔断机制会自动终止该查询&#xff0c;以避免对系统性能、资源使用以及其他查询造成不利影响。这种机制确保了集群在多用户环境下…...

多级缓存和数据一致性问题

一、什么是多级缓存&#xff1f; 多级缓存是一种分层的数据缓存策略&#xff0c;通过在不同层级&#xff08;如本地、分布式、数据库&#xff09;存储数据副本&#xff0c;结合各层缓存的访问速度和容量特性&#xff0c;优化系统的性能和资源利用率。其核心思想是让数据尽可能…...

计算机期刊推荐 | 计算机-人工智能、信息系统、理论和算法、软件工程、网络系统、图形学和多媒体, 工程技术-制造, 数学-数学跨学科应用

Computers, Materials & Continua 学科领域&#xff1a; 计算机-人工智能、信息系统、理论和算法、软件工程、网络系统、图形学和多媒体, 工程技术-制造, 数学-数学跨学科应用 期刊类型&#xff1a; SCI/SSCI/AHCI 收录数据库&#xff1a; SCI(SCIE),EI,Scopus,知网(CNK…...

全书测试:《C++性能优化指南》

以下20道多选题和10道设计题&#xff0c; 用于本书的测试。 以下哪些是C性能优化的核心策略&#xff1f;&#xff08;多选&#xff09; A) 优先优化所有代码段 B) 使用更高效的算法 C) 减少内存分配次数 D) 将所有循环展开 关于字符串优化&#xff0c;正确的措施包括&#xff…...

【教学类-58-14】黑白三角拼图12——单页1页图。参考图1页6张(黑白、彩色)、板式(无圆点、黑圆点、白圆点)、宫格2-10、张数6张,适合集体操作)

背景需求&#xff1a; 基于以下两个代码&#xff0c;设计一个单页1页黑白三角、彩色三角&#xff08;包含黑点、白点、无点&#xff09;的代码。 【教学类-58-12】黑白三角拼图10&#xff08;N张参考图1张操作卡多张彩色白块&#xff0c;适合个别化&#xff09;-CSDN博客文章…...

C++项目:高并发内存池_下

目录 8. thread cache回收内存 9. central cache回收内存 10. page cache回收内存 11. 大于256KB的内存申请和释放 11.1 申请 11.2 释放 12. 使用定长内存池脱离使用new 13. 释放对象时优化成不传对象大小 14. 多线程环境下对比malloc测试 15. 调试和复杂问题的调试技…...

消息队列性能比拼: Kafka vs RabbitMQ

本内容是对知名性能评测博主 Anton Putra Kafka vs RabbitMQ Performance 内容的翻译与整理, 有适当删减, 相关数据和结论以原作结论为准。 简介 在本视频中&#xff0c;我们将首先比较 Apache Kafka 和传统的 RabbitMQ。然后&#xff0c;在第二轮测试中&#xff0c;会将 Kaf…...

AP 场景架构设计(一) :OceanBase 读写分离策略解析

说明&#xff1a;本文内容对应的是 OceanBase 社区版&#xff0c;架构部分不涉及企业版的仲裁副本功能。OceanBase社区版和企业版的能力区别详见&#xff1a; 官网链接。 概述​ 当两种类型的业务共同运行在同一个数据库集群上时&#xff0c;这对数据库的配置等条件提出了较高…...

Java 大视界 -- Java 大数据在智能金融区块链跨境支付与结算中的应用(154)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

手把手教你在linux服务器部署deepseek,打造专属自己的数据库知识库

第一步&#xff1a;安装Ollama 打开官方网址&#xff1a;https://ollama.com/download/linux 下载Ollama linux版本 复制命令到linux操作系统执行 [rootpostgresql ~]# curl -fsSL https://ollama.com/install.sh | sh在Service中增加下面两行 [rootlocalhost ~]# vi /etc/…...

conda极速上手记录

什么是conda: Conda是一个跨平台的包管理工具和环境管理系统&#xff0c;支持Python、R、Java等多种语言。它能解决不同项目间的依赖冲突问题&#xff0c;例如&#xff1a; 项目A需要Python 3.6 NumPy 1.18&#xff1b; 项目B需要Python 3.10 NumPy 2.0。 通过创建独立环境&…...

C++ 继承:面向对象编程的核心概念(一)

文章目录 引言1. 继承的基本知识1.1 继承的关键词的区别1.2 继承类模版 2. 基类和派生类间的转换3. 继承中的作用域4. 派生类的默认成员函数4.1 默认成员函数的规则4.2 自己实现成员函数4.3 实现一个不能被继承的基类&#xff08;基本不用&#xff09; 引言 在C中&#xff0c;…...

蓝桥杯 临时抱佛脚 之 二分答案法与相关题目

二分答案法&#xff08;利用二分法查找区间的左右端点&#xff09; &#xff08;1&#xff09;估计 最终答案可能得范围 是什么 &#xff08;2&#xff09;分析 问题的答案 和 给定条件 之间的单调性&#xff0c;大部分时候只需要用到 自然智慧 &#xff08;3&#xff09;建…...

【图论】网络流算法入门

&#xff08;决定狠狠加训图论了&#xff0c;从一直想学但没启动的网络流算法开始。&#xff09; 网络流问题 • 问题定义&#xff1a;在带权有向图 G ( V , E ) G(V, E) G(V,E) 中&#xff0c;每条边 e ( u , v ) e(u, v) e(u,v) 有容量 c ( u , v ) c(u, v) c(u,v)&am…...

【算法day22】两数相除——给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。

29. 两数相除 给你两个整数&#xff0c;被除数 dividend 和除数 divisor。将两数相除&#xff0c;要求 不使用 乘法、除法和取余运算。 整数除法应该向零截断&#xff0c;也就是截去&#xff08;truncate&#xff09;其小数部分。例如&#xff0c;8.345 将被截断为 8 &#x…...

《TypeScript 7天速成系列》第4天:TypeScript模块与命名空间:大型项目组织之道

在大型TypeScript项目中&#xff0c;良好的代码组织架构是保证项目可维护性的关键。本文将深入探讨TypeScript的模块系统和命名空间&#xff0c;为企业级项目提供最佳实践方案。 一、模块化开发&#xff1a;现代前端工程的基石 1.1 ES模块基础语法 TypeScript全面支持ES6模块…...

AutoCAD C#二次开发中WinForm与WPF的对比

在AutoCAD .NET二次开发中&#xff0c;选择WinForm还是WPF作为用户界面技术&#xff0c;需要根据项目需求、团队技能和AutoCAD版本等因素综合考虑。以下是详细对比&#xff1a; ## 1. 基础特性对比 | 特性 | WinForm | WPF | |------------|…...

关于服务器只能访问localhost:8111地址,局域网不能访问的问题

一、问题来源&#xff1a; 服务器是使用的阿里云的服务器&#xff0c;服务器端的8111端口没有设置任何别的限制&#xff0c;但是在阿里云服务器端并没有设置相应的tcp连接8111端口。 二、解决办法&#xff1a; 1、使用阿里云初始化好的端口&#xff1b;2、配置新的阿里云端口…...

基于ADMM无穷范数检测算法的MIMO通信系统信号检测MATLAB仿真,对比ML,MMSE,ZF以及LAMA

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 ADMM算法 4.2 最大似然ML检测算法 4.3 最小均方误差&#xff08;MMSE&#xff09;检测算法 4.4 迫零&#xff08;ZF&#xff09;检测算法 4.5 OCD_MMSE 检测算法 4.6 LAMA检测算法 …...

Linux 配置时间服务器

一、同步阿里云服务器时间 服务端设置 1.检查chrony服务是否安装&#xff0c;设置chrony开机自启&#xff0c;查看chrony服务状态 [rootnode1-server ~]# rpm -q chrony # rpm -q 用于查看包是否安装 chrony-4.3-1.el9.x86_64 [rootnode1-server ~]# systemctl enable --n…...

可视化web组态开发工具

BY组态是一款功能强大的基于Web的可视化组态编辑器&#xff0c;采用标准HTML5技术&#xff0c;基于B/S架构进行开发&#xff0c;支持WEB端呈现&#xff0c;支持在浏览器端完成便捷的人机交互&#xff0c;简单的拖拽即可完成可视化页面的设计。可快速构建和部署可扩展的SCADA、H…...

深度学习驱动的车牌识别:技术演进与未来挑战

一、引言 1.1 研究背景 在当今社会&#xff0c;智能交通系统的发展日益重要&#xff0c;而车牌识别作为其关键组成部分&#xff0c;发挥着至关重要的作用。车牌识别技术广泛应用于交通管理、停车场管理、安防监控等领域。在交通管理中&#xff0c;它可以用于车辆识别、交通违…...

C++笔记-模板初阶,string(上)

一.模板初阶 1.泛型编程 以往我们要交换不同类型的两个数据就要写不同类型的交换函数&#xff0c;这是使用函数重载虽然可以实现&#xff0c;但是有以下几个不好的地方&#xff1a; 1.重载的函数仅仅是类型不同&#xff0c;代码复用率比较低&#xff0c;只要有新类型出现时&a…...

关于cmd中出现无法识别某某指令的问题

今天来解决以下这个比较常见的问题&#xff0c;安装各种软件都可能会发生&#xff0c;一般是安装时没勾选注册环境变量&#xff0c;导致cmd无法识别该指令。例如mysql&#xff0c;git等&#xff0c;一般初学者可能不太清楚。 解决这类问题最主要的是了解环境变量的概念&#x…...