Android LiveData详解

2024-06-19 11:18
文章标签 android 详解 livedata

本文主要是介绍Android LiveData详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

官方文档翻译

  • 1.LiveData概述
    • 1.1 使用LiveData的优点
    • 1.2 使用LiveData对象
      • 1.2.1 创建LiveData对象
      • 1.2.2 观察LiveData对象
      • 1.2.3 更新LiveData对象
      • 1.2.4 在Room中使用LiveData
    • 1.3 继承LiveData
    • 1.4 LiveData变换
      • 1.4.1 创建新的转换
    • 1.5 合并多个LiveData数据源
    • 1.6 额外资源

1.LiveData概述

LiveData是一个observable数据持有类。与常规observable不同,LiveData是生命周期感知的,这意味着它跟随其他应用程序组件(如activities, fragments, or services)的生命周期。这种感知能力确保LiveData只更新处于活跃生命周期状态的应用程序组件。

注意:在Android工程中导入LiveData组件,请看Adding Components to your Project

LiveData与一个Observer关联,如果观察者的生命周期处于STARTED或RESUMED状态,则表示观察者处于活动状态。LiveData只通知活跃的观察者做更新。注册到LiveData对象中的不活跃的观察者则得不到数据更新的通知。

您可以注册一个observer并与实现了LifecycleOwner接口的对象配对。这种关系允许当相应的Lifecycle对象的状态改变为DESTROYED时,观察者被移除。这对于activities and fragments尤其有用,因为它们可以安全地观察LiveData对象,而不用担心泄漏——当activities 和 fragments的生命周期被销毁时,它们会立即取消订阅。

有关如何使用LiveData的更多信息,请参见使用LiveData对象。

1.1 使用LiveData的优点

使用LiveData具有以下优点:

  1. 确保UI与数据状态匹配

LiveData遵循观察者模式。当生命周期状态改变时,LiveData通知Observer对象。您可以合并代码来更新这些观察者对象中的UI。观察者可以在数据每次有更新时更新UI。

  1. 没有内存泄漏

观察者绑定到生命周期对象,并在其关联的生命周期是destroyed时自行清理。

  1. 停止activities造成的crash问题

如果观察者的生命周期是不活动的,比如在堆栈下面的activity,那么它接收不到任何LiveData事件。

  1. 不再手动管理生命周期

UI组件仅仅观察相关数据,不停止或恢复观察。LiveData自动管理所有这一切,因为在观察的时候它能感知到相关的生命周期状态变化。

  1. 始终保持最新数据

如果生命周期变得不活动,则在再次激活时接收最新数据。例如,后台中的activity在返回到前台后立即接收最新数据。

  1. 及时响应配置改变

如果由于配置改变(如设备旋转)而重新创activity或fragment,则它立即接收最新的可用数据。

  1. 资源共享

您可以使用Sigelon模式集成LiveData对象来包装系统服务,以便它们可以在您的应用程序中共享。一旦LiveData对象连接到系统服务,然后需要用到该资源的任何observer都可以观察到LiveData对象。有关更多信息,请参见扩展LiveData。

1.2 使用LiveData对象

按照以下步骤使用LiveData对象:

创建一个LiveData实例来保存某种类型的数据。这通常是在ViewModel类中完成的。

创建一个Observer对象,该对象定义onChanged()方法,该方法响应LiveData对象中数据更改时发生的变化。通常在UI控制器中创建一个Observer对象,例如activity或fragment。

使用observe()方法将观察者对象与LiveData对象关联到一块。observe()方法使用LifecycleOwner对象。这将Observer对象向LiveData对象订阅,以便通知其更改。通常在UI控制器中添加Observer对象,例如activity 或者 fragment。

注意:您可以使用observeForever(Observer)方法注册一个没有关联LifecycleOwner对象的观察者。在这种情况下,观察者被认为总是活跃的,因此总是被通知更新。您可以通过调用removeObserver(Observer)方法删除这些观察者。

当更新LiveData对象中存储的值时,只要所依附LifecycleOwner处于活动状态,就会触发所有已注册的观察者。

LiveData允许UI控制器观察者订阅更新。当LiveData对象保存的数据发生变化时,UI会自动响应更新。

1.2.1 创建LiveData对象

LiveData是一种可以与任何数据一起使用的包装器,包括实现Collections的对象,如List。LiveData对象通常存储在ViewModel对象中,并通过getter方法访问,如下面的示例所示:

public class NameViewModel extends ViewModel {// Create a LiveData with a String
private MutableLiveData<String> mCurrentName;public MutableLiveData<String> getCurrentName() {if (mCurrentName == null) {mCurrentName = new MutableLiveData<String>();}return mCurrentName;}// Rest of the ViewModel...
}

最初,LiveData对象中的数据没有设置。

注意:请确保将更新UI的LiveData对象存储在ViewModel对象中,而不是activity 或者 fragment中,原因如下:

  • 避免臃肿的activities和fragments。现在这些UI控制器负责显示数据,但不保存数据状态。

  • 将LiveData实例与特定activity 或者 fragment实例解耦, 并允许LiveData对象在配置更改时存活。

您可以ViewModel向导中关于ViewModel类的优点和用法。

1.2.2 观察LiveData对象

在大多数情况下,应用程序组件的onCreate()方法是开始观察LiveData对象的正确位置,原因如下:

以确保系统不从activity或fragment的onResume()方法中进行多余调用。

以确保activity或fragment具有可在其活跃后立即显示的数据。一旦应用程序组件处于STARTED状态,它就会从正在观察的LiveData对象接收最新的值。只有在LiveData对象被设置为可观察状态时才会发生。

通常,LiveData只在数据更改时才提供更新,并且只对活动的observers提供更新。这种行为的一个例外是,观察者在从非活动状态转变为活动状态时也会收到更新。此外,如果观察者第二次从非活动状态改变为活动状态,它仅接收到一个更新,如果自上一次变为活动状态的值发生了改变。

下面的示例代码说明如何开始观察LiveData对象:

public class NameActivity extends AppCompatActivity {private NameViewModel mModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// Other code to setup the activity...// Get the ViewModel.mModel = ViewModelProviders.of(this).get(NameViewModel.class);// Create the observer which updates the UI.final Observer<String> nameObserver = new Observer<String>() {@Overridepublic void onChanged(@Nullable final String newName) {// Update the UI, in this case, a TextView.mNameTextView.setText(newName);}};// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.mModel.getCurrentName().observe(this, nameObserver);}
}

使用作为参数传递的nameObserver调用observe()之后,立即调用onChanged()以提供存储在mCurrentName中的最新值。如果LiveData对象没有在mCurrentName中设置值,则不调用onChanged()。

1.2.3 更新LiveData对象

LiveData没有公开可用的方法来更新存储的数据。MutableLiveData类公开了setValue(T)和postValue(T)方法,如果需要编辑LiveData对象中存储的值,则必须使用这些方法。通常在ViewModel中使用MutableLiveData,然后ViewModel只向observers公开不可变的LiveData对象。

在建立了观察者关系之后,然后可以更新LiveData对象的值,如下面的示例所示,当用户点击按钮时触发所有观察者:

mButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {String anotherName = "John Doe";mModel.getCurrentName().setValue(anotherName);}
});

调用实例中的setValue(T)并传递John Doe作为参数会导致观察者调用它们的onChanged()方法。该示例显示了按下按钮,但是由于各种原因,可以调用setValue()postValue()来更新mName,包括响应网络请求或数据库加载完成;在所有情况下,对setValue()或postValue()的调用都会触发观察者并更新UI。

注意:您必须调用setValue(T) 方法来从主线程更新LiveData对象。如果在工作线程中执行代码,则可以使用postValue(T)方法来更新LiveData对象。

1.2.4 在Room中使用LiveData

Room持久性库支持可观察的查询,这些查询返回LiveData对象。Observable查询是作为数据库访问对象(DAO)的一部分编写的。

当数据库被更新时,Room生成所有必要的代码来更新LiveData对象。生成的代码在需要时异步地在后台线程上运行查询。此模式对于保持UI中显示的数据与存储在数据库中的数据同步是有用的。您可以关于Room和DAOs在Room持久库指南。

1.3 继承LiveData

如果观察者的生命周期处于STARTED或RESUMED状态,则LiveData认为观察者处于活动状态。

public class StockLiveData extends LiveData<BigDecimal> {private StockManager mStockManager;private SimplePriceListener mListener = new SimplePriceListener() {@Overridepublic void onPriceChanged(BigDecimal price) {setValue(price);}};public StockLiveData(String symbol) {mStockManager = new StockManager(symbol);}@Overrideprotected void onActive() {mStockManager.requestPriceUpdates(mListener);}@Overrideprotected void onInactive() {mStockManager.removeUpdates(mListener);}
}

本例中价格listener的实现包括以下重要方法:

当LiveData对象具有活动的观察者时, 调用onActive()方法。这意味着你需要从这个方法开始观察股票价格的更新。

LiveData对象没有任何活动的观察者时调用onInactive()方法。由于没有观察者在监听,所以没有理由保持与StockManager服务的连接。

setValue(T) 方法更新LiveData实例的值,并将变化通知给任何活动的观察者。

您可以使用StockLiveData类如下:

public class MyFragment extends Fragment {@Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);LiveData<BigDecimal> myPriceListener = ...;myPriceListener.observe(this, price -> {// Update the UI.});}
}

observe()方法将作为LifecycleOwner实例的fragment作为第一个参数传递。这样做意味着这个观察者被绑定到与所有者相关联的Lifecycle对象,意思是:

  • 如果生命周期对象不处于活动状态,则即使值改变,也不会调用observer。

  • 在生命周期对象被销毁后,观察者将被自动移除。

LiveData对象是生命周期感知的,这意味着您可以在多个activities, fragments, 和 services之间共享它们。为了保持示例简单,可以将LiveData类作为单例的实现如下:

public class StockLiveData extends LiveData<BigDecimal> {private static StockLiveData sInstance;private StockManager mStockManager;private SimplePriceListener mListener = new SimplePriceListener() {@Overridepublic void onPriceChanged(BigDecimal price) {setValue(price);}};@MainThreadpublic static StockLiveData get(String symbol) {if (sInstance == null) {sInstance = new StockLiveData(symbol);}return sInstance;}private StockLiveData(String symbol) {mStockManager = new StockManager(symbol);}@Overrideprotected void onActive() {mStockManager.requestPriceUpdates(mListener);}@Overrideprotected void onInactive() {mStockManager.removeUpdates(mListener);}
}

你可以在片段中使用它如下:

public class MyFragment extends Fragment {@Overridepublic void onActivityCreated(Bundle savedInstanceState) {StockLiveData.get(getActivity()).observe(this, price -> {// Update the UI.});}
}

多个fragments和activities可以观察MyPriceListener实例。LiveData只连接系统服务,如果其中一个或多个系统可见和激活。

1.4 LiveData变换

在将LiveData对象分发给观察者之前,您可能需要对存储在LiveData对象中的值进行更改,或者您可能需要基于另一个LiveData对象的值返回不同的LiveData实例。Lifecycle包提供转换类,其中包含支持这些方案的帮助方法。

Transformations.map()

对存储在LiveData对象中的值应用函数,并将结果传播到下游。

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

Transformations.switchMap()

类似于map(),将函数应用到LiveData对象中存储的值中,并解包并将结果分发到下游。传递到switchMap()的函数必须返回一个LiveData对象,如下面的示例所示:

private LiveData<User> getUser(String id) {...;
}LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

可以使用transformation方法在观察者的生命周期中传递信息。除非观察者正在观看返回的LiveData对象,否则这些转换不会被计算。因为转换是惰性计算的,所以与生命周期相关的行为是隐式传递的,而不需要额外的显式调用或依赖。

如果您认为在ViewModel对象中需要一个Lifecycle对象,转换可能是更好的解决方案。例如,假设您有一个UI组件,它接受一个地址并返回该地址的邮政编码。可以通过以下示例代码说明该组件的初级的ViewModel模型:

class MyViewModel extends ViewModel {private final PostalCodeRepository repository;public MyViewModel(PostalCodeRepository repository) {this.repository = repository;}private LiveData<String> getPostalCode(String address) {// DON'T DO THISreturn repository.getPostCode(address);}
}

然后,UI组件需要从以前的LiveData对象中注销注册,并在每次调用getPostalCode()时注册到新实例。此外,如果UI组件被重新创建,它将触发对repository.getPostCode()方法的另一个调用,而不是使用前一个调用的结果。

相反,您可以实现postalcode 查询作为地址输入的转换,如下面的示例所示:

class MyViewModel extends ViewModel {private final PostalCodeRepository repository;private final MutableLiveData<String> addressInput = new MutableLiveData();public final LiveData<String> postalCode =Transformations.switchMap(addressInput, (address) -> {return repository.getPostCode(address);});public MyViewModel(PostalCodeRepository repository) {this.repository = repository}private void setInput(String address) {addressInput.setValue(address);}
}

在这种情况下,postalCode字段是publicfinal,因为字段从不更改。postalCode字段定义为addressInput的转换,这意味着在addressInput更改时调用repository.getPostCode()方法。如果存在活动观察者,则这是正确的,如果在调用repository.getPostCode()时没有活动观察者,则在添加观察者之前不进行任何计算。

该机制允许应用程序的较低级别创建按需求计算的LiveData对象。ViewModel对象可以很容易地获得对LiveData对象的引用,然后在它们上面定义转换规则。

1.4.1 创建新的转换

在你的应用程序中有十几种不同的特定转换,但默认情况下它们不被提供。为了实现您自己的转换,您可以使用MediatorLiveData类,该类侦听其他LiveData对象并处理它们发出的事件。MediatorLiveData正确地将其状态传播到Source LiveData对象。若要了解此模式的更多信息,请参见Transformations的参考文档。

1.5 合并多个LiveData数据源

MediatorLiveData是LiveData的子类,允许您合并多个LiveData源。然后,每当原始LiveData源对象改变时,就会触发MediatorLiveData对象的观察者。

例如,如果在UI中有一个可以从本地数据库或网络更新的LiveData对象,则可以向MediatorLiveData对象添加以下源:

  • 与数据库中存储的数据相关联的LiveData对象。

  • 与从网络访问的数据相关联的LiveData对象。

您的activity只需要观察MediatorLiveData对象以从两个源接收更新。有关详细示例,请参阅附录:App体系结构指南中公开网络状态部分。

1.6 额外资源

LiveData是一个Android Jetpack架构组件。在Sunflower demo应用程序中使用它。

有关使用LiveData与Snackbar消息、navigation事件和其他事件有关的信息,请阅读此帖子。

这篇关于Android LiveData详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1074879

相关文章

Python函数作用域示例详解

《Python函数作用域示例详解》本文介绍了Python中的LEGB作用域规则,详细解析了变量查找的四个层级,通过具体代码示例,展示了各层级的变量访问规则和特性,对python函数作用域相关知识感兴趣... 目录一、LEGB 规则二、作用域实例2.1 局部作用域(Local)2.2 闭包作用域(Enclos

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE

MySQL中的锁机制详解之全局锁,表级锁,行级锁

《MySQL中的锁机制详解之全局锁,表级锁,行级锁》MySQL锁机制通过全局、表级、行级锁控制并发,保障数据一致性与隔离性,全局锁适用于全库备份,表级锁适合读多写少场景,行级锁(InnoDB)实现高并... 目录一、锁机制基础:从并发问题到锁分类1.1 并发访问的三大问题1.2 锁的核心作用1.3 锁粒度分

MySQL数据库中ENUM的用法是什么详解

《MySQL数据库中ENUM的用法是什么详解》ENUM是一个字符串对象,用于指定一组预定义的值,并可在创建表时使用,下面:本文主要介绍MySQL数据库中ENUM的用法是什么的相关资料,文中通过代码... 目录mysql 中 ENUM 的用法一、ENUM 的定义与语法二、ENUM 的特点三、ENUM 的用法1

MySQL count()聚合函数详解

《MySQLcount()聚合函数详解》MySQL中的COUNT()函数,它是SQL中最常用的聚合函数之一,用于计算表中符合特定条件的行数,本文给大家介绍MySQLcount()聚合函数,感兴趣的朋... 目录核心功能语法形式重要特性与行为如何选择使用哪种形式?总结深入剖析一下 mysql 中的 COUNT

一文详解Git中分支本地和远程删除的方法

《一文详解Git中分支本地和远程删除的方法》在使用Git进行版本控制的过程中,我们会创建多个分支来进行不同功能的开发,这就容易涉及到如何正确地删除本地分支和远程分支,下面我们就来看看相关的实现方法吧... 目录技术背景实现步骤删除本地分支删除远程www.chinasem.cn分支同步删除信息到其他机器示例步骤

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁