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

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

嵌入式Openharmony系统构建与启动详解

大家好,今天主要给大家分享一下,如何构建Openharmony子系统以及系统的启动过程分解。 第一:OpenHarmony系统构建      首先熟悉一下,构建系统是一种自动化处理工具的集合,通过将源代码文件进行一系列处理,最终生成和用户可以使用的目标文件。这里的目标文件包括静态链接库文件、动态链接库文件、可执行文件、脚本文件、配置文件等。      我们在编写hellowor

LabVIEW FIFO详解

在LabVIEW的FPGA开发中,FIFO(先入先出队列)是常用的数据传输机制。通过配置FIFO的属性,工程师可以在FPGA和主机之间,或不同FPGA VIs之间进行高效的数据传输。根据具体需求,FIFO有多种类型与实现方式,包括目标范围内FIFO(Target-Scoped)、DMA FIFO以及点对点流(Peer-to-Peer)。 FIFO类型 **目标范围FIFO(Target-Sc