Android 架构组件之 LiveData

2024-02-25 16:58
文章标签 android 组件 架构 livedata

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

文章目录

    • 1. LiveData 是什么?为什么要使用 LiveData?
      • 1.1 一个示例,看使用 LiveData 的优势
    • 2. 分析 LiveData 的组成及实现原理
      • 2.1 通过类图分析 LiveData 的组成
      • 2.2 分析 LiveData 是怎样感知组件生命周期的
      • 2.3 数据更新时 LiveData 又是怎样通知组件的
    • 3. LiveData 中的转换方法
      • 3.1 map
      • 3.2 switchMap
      • 3.3 MediatorLiveData
    • 4. 总结使用 LiveData 的步骤
    • 参考链接

LiveData 是一种具有生命周期感知能力的、可观察数据的、持有类。本文将从以下几个方面来介绍 LiveData:

  • LiveData 是什么?为什么要使用 LiveData?
  • 分析 LiveData 的组成及实现原理
  • 介绍 LiveData 中的转换方法
  • 总结使用 LiveData 的步骤

1. LiveData 是什么?为什么要使用 LiveData?

LiveData 可以简单的理解为具有感知生命周期能力的容器。生命周期感知能力的实现,得益于 Android Framework 使用了一下 Lifecycles 中的类。关于 Lifecycle 的详细介绍,可以查看此前的一篇文章 Android 架构组件之 Lifecycle

这里我们简单的总结一下,Lifecycle 组件包括三个组成部分:

  • Lifecycle:Lifecycle是一个定义 Android 生命周期及状态的类。
  • LifecycleOwner 接口,用于连接有生命周期的对象,例如 AppCompatActivity 和 Fragment。
  • LifecycleObserver,用于观察 LifecycleOwner 的接口。

而我们要介绍的 LiveData,是一个 LifecycleObserver,它可以直接感知 Activity 或 Fragment 的生命周期。基于此,使得 LiveData 较其它的容器类,有一下两个优势:

  • 如果Activity 不在屏幕上,LiveData 不会触发没有必要的界面刷新;
  • 如果 Activity 已经被销毁,LiveData 将自动清空与 Observer 的连接。这样屏幕外或者已经销毁 Activity 或 Fragment就不会被意外地调用。

1.1 一个示例,看使用 LiveData 的优势

下面来看一个示例。假设有一个 UI 界面和 LiveData 对象,这个对象保存了你想要在屏幕上显示的数据,界面可以声明“观察”LiveData 对象,也就是说,界面希望在 LiveData 有更新时收到通知,随后界面会使用新数据重新进行绘制。简而言之,LiveData 可以使屏幕上显示的内容与数据随时保持同步。 看示例代码:

LiveData 对象通常保存在 ViewModel 类中,关于 ViewModel 的介绍,可以查看Android 架构组件之 ViewModel

class LiveDataTimerViewModel : ViewModel() {private val mElapsedTime = MutableLiveData<Long>()private var mInitialTime: Long = 0private var timer: Timer? = nullinit {mInitialTime = SystemClock.elapsedRealtime()timer = Timer()// Update the elapsed time every second.timer?.scheduleAtFixedRate(object : TimerTask() {override fun run() {val newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000// setValue() cannot be called from a background thread so post to main thread.mElapsedTime.postValue(newValue)}}, ONE_SECOND, ONE_SECOND)}fun getElapsedTime(): LiveData<Long> = mElapsedTime

在 ViewModel 中,声明一个 MutableLiveData 类型的成员变量,用来保存屏幕上要显示的数据,通过 Timer 类,每秒钟更新一次 LiveData 中的数据。

接下来,在 Activity 的 onCreate()方法中,我们可以从 ViewModel 中获取 LiveData,并在 LiveData 上调用 observe()方法,方法中的第一个参数为 Context,在这里为当下的 Activity,第二个参数是“Observer 观察者”,属于方法回调,回调之后界面会被更新,如果需要更新 LiveData,可以调用 setValue()方法或 postValue()方法。

override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.chrono_activity_3)mLiveDataTimerViewModel = ViewModelProvider(this).get(LiveDataTimerViewModel::class.java)subscribe()}private fun subscribe() {val elapsedTimeObserver = Observer<Long> {val newText = resources.getString(R.string.seconds, it)timer_textview.text = newTextLog.d("ChronoActivity3", "Updating timer");}mLiveDataTimerViewModel?.getElapsedTime()?.observe(this, elapsedTimeObserver)}

完整的示例代码,在文末给出 GitHub 地址。

setValue()方法或 postValue()方法,两者的区别在于,setValue 只可以在主线程上运行,postValue 只可以在后台线程上运行,调用 setValue 和 postValue 时,LiveData 将通知 Observer,并更新界面。

以上是LiveData的基本介绍以及使用它的优势,除此之外。Room 数据框架可以很友好地支持 LiveData,Room 返回的 LiveData 对象,可以在数据库数据变化时自动收到通知,还可以在后台线程中加载数据,这样我们可以在数据库更新的时候,轻松地更新界面。关于 Room 组件的介绍,可以查看我们的系列文章。

2. 分析 LiveData 的组成及实现原理

2.1 通过类图分析 LiveData 的组成

我们用最新的 lifecycle-livedata:2.2.0-alpha02 版本来分析其原理,先看一张类图。

通过上面的类图,我们可以看到:
  • 在 LiveData 中定义了三个内部类 ObserverWrapper、LifecycleBoundObserver 和 AlwaysActiveObserver。

  • ObserverWrapper 是 Observer 的一个包装类,在该类中会记录组件是否处在激活状态的信息,shouldBeActive() 方法是一个抽象方法,需要子类去实现,activeStateChanged() 方法会根据组件的状态,回调不同的方法,后面会详细介绍。

  • LifecycleBoundObserver 和 AlwaysActiveObserver 是 ObserverWrapper的子类,LiveData 对组件的注册监听,都是通过这两个类完成的,区别是当使用 observe()方法注册时使用的是 LifecycleBoundObserver,使用observeForever()方法注册时使用的是 AlwaysActiveObserver。LifecycleBoundObserver 还实现了 LifecycleEventObserver 接口,这样LifecycleBoundObserver 就可以通过 onStateChanged()回调方法来监测组件生命周期的变化了。

  • LiveData 是一个抽象类,它有两个子类 MutableLiveData 和 MediatorLiveData,在 LiveData 中有个两个用来完成对组件注册监听的方法 observe() 和 observeForever();有注册监听的方法,就有取消注册监听的方法 removeObserver() 和 removeObservers();因为 LiveData 是一个容器,所以提供了两个设置 Value 的方法 postValue() 和 setValue();onActive() 和 onInactive()是两个回调方法。

  • LiveData 中的两个回调方法 onActive()和 onInactive(),当LiveData 监听的处于激活状态的组件中 observer的个数不为 0 时会调用回调函数onActive(),当 LiveData监听的组件没有处于激活状态,或者组件中 observer 的个数为 0 时调用onInactive()回调函数。

2.2 分析 LiveData 是怎样感知组件生命周期的

在上面的类图中,我们了解了跟 LiveData 相关的类,下面我们来看一下 LiveData 是怎样感知组件生命周期的,当数据更新时又是怎样通知组件的。

先来看 LiveData 是怎样感知组件生命周期的,上面提到了在 LiveData 中有两个用来完成对组件注册监听的方法 observe() 和 observeForever(),先来看 observe()方法:

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {assertMainThread("observe");if (owner.getLifecycle().getCurrentState() == DESTROYED) {// ignorereturn;}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对象,组件 Activity 和 Fragment 都是实现了LifecycleOwner接口,所以这里的owner可以理解为组件对象。在方法内部创建了LifecycleBoundObserver 对象 wrapper,通过

owner.getLifecycle().addObserver(wrapper)

完成了对组件生命周期的监听,当组件生命周期发生变化时都会回调 wrapper 对象中的onStateChanged()方法,再来看一下 LifecycleBoundObserver 的实现:

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {@NonNullfinal LifecycleOwner mOwner;LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {super(observer);mOwner = owner;}@Overrideboolean shouldBeActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);}@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {removeObserver(mObserver);return;}activeStateChanged(shouldBeActive());}@Overrideboolean isAttachedTo(LifecycleOwner owner) {return mOwner == owner;}@Overridevoid detachObserver() {mOwner.getLifecycle().removeObserver(this);}}

可以看到在 LifecycleBoundObserver 中有一个 shouldBeActive()方法,这个方法会在组件状态是STARTED 和 RESUMED 时返回 true。

在 onStateChanged() 方法中会调用到 activeStateChanged()方法,该方法接收一个 boolean 型参数,当参数值为 true 时会调用dispatchingValue()方法,这个方法最终会调用 Observer 的onChanged()回调方法。这也就解释了使用 observe()方法对组件进行监听,只有组件在激活状态STARTED、RESUMED时(对应生命周期 onStart、onResume、onPause)时,才会收到数据更新的通知。当组件在DESTROYED状态时,在onStateChanged()方法内部会调用removeObserver(),因此我们在使用 observe()方法时,可以不考虑 observer 的销毁。

void activeStateChanged(boolean newActive) {if (newActive == mActive) {return;}// immediately set active state, so we'd never dispatch anything to inactive// ownermActive = newActive;boolean wasInactive = LiveData.this.mActiveCount == 0;LiveData.this.mActiveCount += mActive ? 1 : -1;if (wasInactive && mActive) {onActive();}if (LiveData.this.mActiveCount == 0 && !mActive) {onInactive();}if (mActive) {dispatchingValue(this);}}

我们前面提到使用 observeForever 注册时需要手动取消,来看看是为什么。

@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {assertMainThread("observeForever");AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);if (existing instanceof LiveData.LifecycleBoundObserver) {throw new IllegalArgumentException("Cannot add the same observer"+ " with different lifecycles");}if (existing != null) {return;}wrapper.activeStateChanged(true);}

observeForever() 方法不再接收 LifecycleOwner 对象,在AlwaysActiveObserver 类中的 shouldBeActive()方法的返回值一直是 true,所以使用该方法进行注册监听时,只要数据有变化,不论组件处在什么状态,都会收到通知,这也是为什么要在退出组件时,需要手动调用 removeXXX()方法将注册的 observer 移除。

private class AlwaysActiveObserver extends ObserverWrapper {AlwaysActiveObserver(Observer<? super T> observer) {super(observer);}@Overrideboolean shouldBeActive() {return true;}}

2.3 数据更新时 LiveData 又是怎样通知组件的

了解了 LiveData 是怎样感知组件生命周期的,下面再来看一下当数据更新时又是怎样通知组件的。LiveData 提供了两个方法来更新数据,setValue() 方法和 postValue() 方法。区别是 setValue() 方法要在主线程中调用,而 postValue() 方法既可在主线程也可在子线程中调用。先来看一下 setValue() 方法。

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

setValue() 方法中会调用 dispatchingValue() 方法,这个方法最终会调用observe() 或者 observeForever() 传入的Observer对象的 onChanged()回调方法。这就解释了当数据更新时是怎样发送通知的。再来看一下 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() 方法通过 ArchTaskExecutor 实现了在主线程中执行mPostValueRunnable 对象中的内容,而在mPostValueRunnable中最终会调用setValue() 方法来实现改变 LiveData 存储的数据。

至此,我们知道了 LiveData 是怎样感知组件生命周期的,以及当数据更新时又是怎样通知组件的。

3. LiveData 中的转换方法

LiveData 还提供各种转换方法,包括: map() 、switchMap() 和 MediatorLiveData。

3.1 map

map()方法,可转换 LiveData 的输出,map 可以将 LiveData A 的方法传递到 LiveData B。

比如,上面的示例中将 LiveData 中的 Long 类型,通过 map 方法转换为 String 类型。

var userNameLiveData = Transformations.map(mElapsedTime, { time ->"$time 秒"}

3.2 switchMap

switchMap() 更改被 LvieData 观察的对象,switchMap与 map 很相似,区别在于,switchMap 给出的是 LiveData,而 Map 给出的是具体值。

举个例子,假设在 Room 数据库中存有很多用户,我们有一个查找数据库用户的方法,我们可以通过 switchMap 将用户 ID与 LiveData 相连,当界面需要查找的 ID 发生变化时,会重新调用查找用户的方法,调用之后生成 LiveData。现在将引用到新找到的用户的 LiveData,这样不管需要查找多少个用户的 LiveData,界面都只需要观察生成的 LiveData 一次。这就是 switchMap 的强大之处。

3.3 MediatorLiveData

MediatorLiveData:用于自定义数据转换,它可以添加或者移除原来的 LiveData 对象,然后多个原 LiveData 对象可以进行组合,作为单一 LiveData 向下传递。

4. 总结使用 LiveData 的步骤

我们先来总结一下使用 LiveData 的步骤:

  • 创建 LiveData 实例以存储某种类型的数据,通常在 ViewModel 类中完成。

  • 创建可定义 onChanged() 方法的 Observer 对象,该方法在 LiveData 对象存储的数据更改时会被调用。通常情况下,在界面(如 Activity 或 Fragment)中创建 Observer 对象。

  • 使用 observe() 方法将 Observer 对象附加到 LiveData 对象。这样会使 Observer 对象订阅 LiveData 对象,以使其收到有关更改的通知。通常情况下,您可以在界面(如 Activity 或 Fragment)中附加 Observer 对象。

LiveData使用起来非常简单,它可以被观察,可以感知生命周期,在很多情况下都能够被灵活运用。

更多内容,可以订阅 我的博客


参考链接

LiveData

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



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

相关文章

C++ RabbitMq消息队列组件详解

《C++RabbitMq消息队列组件详解》:本文主要介绍C++RabbitMq消息队列组件的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. RabbitMq介绍2. 安装RabbitMQ3. 安装 RabbitMQ 的 C++客户端库4. A

PyQt6中QMainWindow组件的使用详解

《PyQt6中QMainWindow组件的使用详解》QMainWindow是PyQt6中用于构建桌面应用程序的基础组件,本文主要介绍了PyQt6中QMainWindow组件的使用,具有一定的参考价值,... 目录1. QMainWindow 组php件概述2. 使用 QMainWindow3. QMainW

Maven 插件配置分层架构深度解析

《Maven插件配置分层架构深度解析》:本文主要介绍Maven插件配置分层架构深度解析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Maven 插件配置分层架构深度解析引言:当构建逻辑遇上复杂配置第一章 Maven插件配置的三重境界1.1 插件配置的拓扑

Android NDK版本迭代与FFmpeg交叉编译完全指南

《AndroidNDK版本迭代与FFmpeg交叉编译完全指南》在Android开发中,使用NDK进行原生代码开发是一项常见需求,特别是当我们需要集成FFmpeg这样的多媒体处理库时,本文将深入分析A... 目录一、android NDK版本迭代分界线二、FFmpeg交叉编译关键注意事项三、完整编译脚本示例四

Android与iOS设备MAC地址生成原理及Java实现详解

《Android与iOS设备MAC地址生成原理及Java实现详解》在无线网络通信中,MAC(MediaAccessControl)地址是设备的唯一网络标识符,本文主要介绍了Android与iOS设备M... 目录引言1. MAC地址基础1.1 MAC地址的组成1.2 MAC地址的分类2. android与I

Android 实现一个隐私弹窗功能

《Android实现一个隐私弹窗功能》:本文主要介绍Android实现一个隐私弹窗功能,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 效果图如下:1. 设置同意、退出、点击用户协议、点击隐私协议的函数参数2. 《用户协议》、《隐私政策》设置成可点击的,且颜色要区分出来res/l

Android实现一键录屏功能(附源码)

《Android实现一键录屏功能(附源码)》在Android5.0及以上版本,系统提供了MediaProjectionAPI,允许应用在用户授权下录制屏幕内容并输出到视频文件,所以本文将基于此实现一个... 目录一、项目介绍二、相关技术与原理三、系统权限与用户授权四、项目架构与流程五、环境配置与依赖六、完整

Android 12解决push framework.jar无法开机的方法小结

《Android12解决pushframework.jar无法开机的方法小结》:本文主要介绍在Android12中解决pushframework.jar无法开机的方法,包括编译指令、框架层和s... 目录1. android 编译指令1.1 framework层的编译指令1.2 替换framework.ja

Android开发环境配置避坑指南

《Android开发环境配置避坑指南》本文主要介绍了Android开发环境配置过程中遇到的问题及解决方案,包括VPN注意事项、工具版本统一、Gerrit邮箱配置、Git拉取和提交代码、MergevsR... 目录网络环境:VPN 注意事项工具版本统一:android Studio & JDKGerrit的邮

Android实现定时任务的几种方式汇总(附源码)

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel