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

相关文章

MySQL 日期时间格式化函数 DATE_FORMAT() 的使用示例详解

《MySQL日期时间格式化函数DATE_FORMAT()的使用示例详解》`DATE_FORMAT()`是MySQL中用于格式化日期时间的函数,本文详细介绍了其语法、格式化字符串的含义以及常见日期... 目录一、DATE_FORMAT()语法二、格式化字符串详解三、常见日期时间格式组合四、业务场景五、总结一、

Qt实现发送HTTP请求的示例详解

《Qt实现发送HTTP请求的示例详解》这篇文章主要为大家详细介绍了如何通过Qt实现发送HTTP请求,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、添加network模块2、包含改头文件3、创建网络访问管理器4、创建接口5、创建网络请求对象6、创建一个回复对

Apache伪静态(Rewrite).htaccess文件详解与配置技巧

《Apache伪静态(Rewrite).htaccess文件详解与配置技巧》Apache伪静态(Rewrite).htaccess是一个纯文本文件,它里面存放着Apache服务器配置相关的指令,主要的... 一、.htAccess的基本作用.htaccess是一个纯文本文件,它里面存放着Apache服务器

Java中有什么工具可以进行代码反编译详解

《Java中有什么工具可以进行代码反编译详解》:本文主要介绍Java中有什么工具可以进行代码反编译的相关资,料,包括JD-GUI、CFR、Procyon、Fernflower、Javap、Byte... 目录1.JD-GUI2.CFR3.Procyon Decompiler4.Fernflower5.Jav

golang panic 函数用法示例详解

《golangpanic函数用法示例详解》在Go语言中,panic用于触发不可恢复的错误,终止函数执行并逐层向上触发defer,最终若未被recover捕获,程序会崩溃,recover用于在def... 目录1. panic 的作用2. 基本用法3. recover 的使用规则4. 错误处理建议5. 常见错

pycharm远程连接服务器运行pytorch的过程详解

《pycharm远程连接服务器运行pytorch的过程详解》:本文主要介绍在Linux环境下使用Anaconda管理不同版本的Python环境,并通过PyCharm远程连接服务器来运行PyTorc... 目录linux部署pytorch背景介绍Anaconda安装Linux安装pytorch虚拟环境安装cu

一文详解如何在Python中使用Requests库

《一文详解如何在Python中使用Requests库》:本文主要介绍如何在Python中使用Requests库的相关资料,Requests库是Python中常用的第三方库,用于简化HTTP请求的发... 目录前言1. 安装Requests库2. 发起GET请求3. 发送带有查询参数的GET请求4. 发起PO

Python进行PDF文件拆分的示例详解

《Python进行PDF文件拆分的示例详解》在日常生活中,我们常常会遇到大型的PDF文件,难以发送,将PDF拆分成多个小文件是一个实用的解决方案,下面我们就来看看如何使用Python实现PDF文件拆分... 目录使用工具将PDF按页数拆分将PDF的每一页拆分为单独的文件将PDF按指定页数拆分根据页码范围拆分

Java中的Cursor使用详解

《Java中的Cursor使用详解》本文介绍了Java中的Cursor接口及其在大数据集处理中的优势,包括逐行读取、分页处理、流控制、动态改变查询、并发控制和减少网络流量等,感兴趣的朋友一起看看吧... 最近看代码,有一段代码涉及到Cursor,感觉写法挺有意思的。注意是Cursor,而不是Consumer

SpringBoot项目注入 traceId 追踪整个请求的日志链路(过程详解)

《SpringBoot项目注入traceId追踪整个请求的日志链路(过程详解)》本文介绍了如何在单体SpringBoot项目中通过手动实现过滤器或拦截器来注入traceId,以追踪整个请求的日志链... SpringBoot项目注入 traceId 来追踪整个请求的日志链路,有了 traceId, 我们在排