Android架构组件之LiveData
来源:互联网 发布:编程之魂 译者 编辑:程序博客网 时间:2024/05/21 05:04
基本概念
LiveData是一个可以被观察的数据持有类,它可以感知并遵循Activity、Fragment或Service等组件的生命周期。正是由于LiveData对组件生命周期可感知特点,因此可以做到仅在组件处于生命周期的激活状态时才更新UI数据。
LiveData需要一个观察者对象,一般是Observer
类的具体实现。当观察者的生命周期处于STARTED
或RESUMED
状态时,LiveData会通知观察者数据变化;在观察者处于其他状态时,即使LiveData的数据变化了,也不会通知。
LiveData可以使用如下方法注册观察者:
@MainThreadpublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {}
第一个参数为LifecycleOwner
,在新的兼容库中,Activity、Fragment等都实现了LifecycleOwner
接口。LifecycleOwner
可以获取Lifecycle
,从而得到组件的各生命周期状态。下面是LifecycleOwner
的源码定义:
/** * A class that has an Android lifecycle. These events can be used by custom components to * handle lifecycle changes without implementing any code inside the Activity or the Fragment. * * @see Lifecycle */@SuppressWarnings({"WeakerAccess", "unused"})public interface LifecycleOwner { /** * Returns the Lifecycle of the provider. * * @return The lifecycle of the provider. */ @NonNull Lifecycle getLifecycle();}
第二个参数为观察者对象,当数据源变化时就会回调。Observer
的源码如下:
/** * A simple callback that can receive from {@link LiveData}. * * @param <T> The type of the parameter * * @see LiveData LiveData - for a usage description. */public interface Observer<T> { /** * Called when the data is changed. * @param t The new data */ void onChanged(@Nullable T t);}
通过LiveData的observe
方法进行关系绑定,就可以在组件的生命周期状态变为DESTROYED
时移除观察者,这样Activity或Fragment就可以安全地观察LiveData而不用担心造成内存泄露。
LiveData的优点
- 确保数据源跟UI展示一致——当数据源变化时,LiveData会通知观察者更新UI,前提是组件在激活状态下。
- 不会造成内存泄露——由于
Observer
跟LifecycleOwner
建立关系,从而可以获取生命周期状态,当组件生命周期状态为DESTROYED
时,会移除观察者。 - 当组件处于非激活状态时,不会收到数据更新回调。
- 无需手动处理生命周期——UI组件只需要观察对应的数据,LiveData根据其生命周期自动处理。
- 总是最新的数据——当Activity从后台切到前台时,总会收到最新的数据。
- 适配设备配置变化——如屏幕旋转,组件销毁重建后,立即收到上次的数据。
- 资源共享——参考【扩展LiveData】一节。
LiveData使用方法
使用LiveData的基本步骤:
- 在ViewModel中创建LiveData,并持有某种类型的数据。
- 创建一个
Observer
对象并实现其onChanged()
回调,一般在Activity或Fragment中创建Observer
。 - 在Activity或Fragment中通过LiveData的
observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer)
方法建立观察关系。
LiveData的
observeForever(@NonNull Observer<T> observer)
方法不需要传入LifecycleOwner
,这意味着是永久观察,无关生命周期状态,任何时候有数据更新都会回调onChanged()
。
创建LiveData
public class NameViewModel extends ViewModel {// Create a LiveData with a Stringprivate MutableLiveData<String> mCurrentName; public MutableLiveData<String> getCurrentName() { if (mCurrentName == null) { mCurrentName = new MutableLiveData<String>(); } return mCurrentName; }// Rest of the ViewModel...}
一般LiveData都会在ViewModel中实现,MutableLiveData继承自LiveData,表示可变数据,提供了数据设置方法。
观察LiveData对象
通常在UI组件的onCreate()
中建立对LiveData的观察关系。下面的代码片段演示了如何观察LiveData对象:
public class NameActivity extends AppCompatActivity { private NameViewModel mModel; @Override protected 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>() { @Override public 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); }}
更新LiveData对象
LiveData没有对外提供公有的修改数据的方法,而其子类MutableLiveData
提供了setValue(T)
(主线程使用)和postValue(T)
(子线程使用)两个方法允许修改数据。MutableLiveData
源码定义如下:
/** * {@link LiveData} which publicly exposes {@link #setValue(T)} and {@link #postValue(T)} method. * * @param <T> The type of data hold by this instance */@SuppressWarnings("WeakerAccess")public class MutableLiveData<T> extends LiveData<T> { @Override public void postValue(T value) { super.postValue(value); } @Override public void setValue(T value) { super.setValue(value); }}
在前几步的基础上,我们通过一个按钮来触发修改LiveData数据,看对应的UI是否得到更新:
mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String anotherName = "John Doe"; mModel.getCurrentName().setValue(anotherName); }});
当触发LiveData的setValue(T)
时,观察者对象的onChanged()
被回调,从而更新UI。
LiveData结合Room使用
- Room 是Android官方的持久化数据库方案,支持返回LiveData对象,当数据库变化时会更新LiveData,这些逻辑由Room自动生成。
- 上述逻辑是在后台异步进行的,这种模式可以使UI跟数据库的数据保持同步。
更多关于Room的用法可参考 Room persistent library guide。
扩展LiveData
通常LiveData只关心观察者的STARTED
或RESUMED
状态,下面代码片段扩展了LiveData:
public class StockLiveData extends LiveData<BigDecimal> { private StockManager mStockManager; private SimplePriceListener mListener = new SimplePriceListener() { @Override public void onPriceChanged(BigDecimal price) { setValue(price); } }; public StockLiveData(String symbol) { mStockManager = new StockManager(symbol); } @Override protected void onActive() { mStockManager.requestPriceUpdates(mListener); } @Override protected void onInactive() { mStockManager.removeUpdates(mListener); }}
来看onActive()
和onInactive()
的定义:
/** * Called when the number of active observers change to 1 from 0. * <p> * This callback can be used to know that this LiveData is being used thus should be kept * up to date. */protected void onActive() {}/** * Called when the number of active observers change from 1 to 0. * <p> * This does not mean that there are no observers left, there may still be observers but their * lifecycle states aren't {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED} * (like an Activity in the back stack). * <p> * You can check if there are observers via {@link #hasObservers()}. */protected void onInactive() {}
注释解释得很清楚,一旦有激活的观察者时就会触发onActive()
,当没有激活的观察者时则会触发onInactive()
。
StockLiveData
的使用如下:
public class MyFragment extends Fragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); LiveData<BigDecimal> myPriceListener = ...; myPriceListener.observe(this, price -> { // Update the UI. }); }}
为了将LiveData在多个Activity、Fragment或Service之间共享,通常设计为单例模式,如下面代码所示:
public class StockLiveData extends LiveData<BigDecimal> { private static StockLiveData sInstance; private StockManager mStockManager; private SimplePriceListener mListener = new SimplePriceListener() { @Override public void onPriceChanged(BigDecimal price) { setValue(price); } }; @MainThread public static StockLiveData get(String symbol) { if (sInstance == null) { sInstance = new StockLiveData(symbol); } return sInstance; } private StockLiveData(String symbol) { mStockManager = new StockManager(symbol); } @Override protected void onActive() { mStockManager.requestPriceUpdates(mListener); } @Override protected void onInactive() { mStockManager.removeUpdates(mListener); }}
LiveData变换
在实际应用中可能有这样一些场景:
- 希望修改LiveData中的数据,然后将修改后的结果发送到观察者。
- 希望基于当前的LiveData返回另一个LiveData对象。
支持库提供了一个Transformations
类,并提供了两个现成的变换方法可以帮助我们实现上述需求。
map()
Transformations.map()
对应源码为:
/** * Applies the given function on the main thread to each value emitted by {@code source} * LiveData and returns LiveData, which emits resulting values. * <p> * The given function {@code func} will be executed on the main thread. * <p> * Suppose that you have a LiveData, named {@code userLiveData}, that contains user data and you * need to display the user name, created by concatenating the first and the last * name of the user. You can define a function that handles the name creation, that will be * applied to every value emitted by {@code useLiveData}. * * @param source a {@code LiveData} to listen to * @param func a function to apply * @param <X> a type of {@code source} LiveData * @param <Y> a type of resulting LiveData. * @return a LiveData which emits resulting values */@MainThreadpublic static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source, @NonNull final Function<X, Y> func) { final MediatorLiveData<Y> result = new MediatorLiveData<>(); result.addSource(source, new Observer<X>() { @Override public void onChanged(@Nullable X x) { result.setValue(func.apply(x)); } }); return result;}
此种变换会通过第二个参数的Function实现对数据的修改,应用示例:
LiveData<User> userLiveData = ...;LiveData<String> userName = Transformations.map(userLiveData, user -> { user.name + " " + user.lastName});
switchMap()
Transformations.switchMap()
对应源码为:
/* * @param trigger a {@code LiveData} to listen to * @param func a function which creates "backing" LiveData * @param <X> a type of {@code source} LiveData * @param <Y> a type of resulting LiveData */@MainThreadpublic static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger, @NonNull final Function<X, LiveData<Y>> func) { final MediatorLiveData<Y> result = new MediatorLiveData<>(); result.addSource(trigger, new Observer<X>() { LiveData<Y> mSource; @Override public void onChanged(@Nullable X x) { LiveData<Y> newLiveData = func.apply(x); if (mSource == newLiveData) { return; } if (mSource != null) { result.removeSource(mSource); } mSource = newLiveData; if (mSource != null) { result.addSource(mSource, new Observer<Y>() { @Override public void onChanged(@Nullable Y y) { result.setValue(y); } }); } } }); return result;}
此种变化就复杂一些了,基于输入LiveData得到另一个LiveData,应用示例:
MutableLiveData<String> userIdLiveData = ...;LiveData<User> userLiveData = Transformations.switchMap(userIdLiveData, id -> repository.getUserById(id));void setUserId(String userId) { this.userIdLiveData.setValue(userId);}
如果上面两种变换无法满足你的需求,可以利用MediatorLiveData
类(是LiveData的子类)来实现自定义的变换方法。更多信息请参考MediatorLiveData。
合并多个LiveData数据源
MediatorLiveData
类可以合并多个LiveData数据源,只要任何一个LiveData数据发生变化,MediatorLiveData
的观察者都会收到通知。
示例场景:
一个Activity界面的UI数据依赖于网络数据源和数据库数据,因此会有两个LiveData。使用MediatorLiveData
将两个LiveData合并后,Activity只需要观察一个MediatorLiveData
即可。无论什么时候,只要任何一个LiveData数据源发生变化,都会通知Activity的UI进行更新。
参考:
https://developer.android.google.cn/topic/libraries/architecture/livedata.html
- Android架构组件之LiveData
- Android架构组件(二)——LiveData
- Android Architecture Component之LiveData
- [译]Android架构组件 – 查看Room和LiveData – 第一部分
- ObjectBox[七] 支持LiveData(Android体系结构组件)
- android LiveData
- Android Architecture Components应用架构组件源码详解(基于1.0以上)(第二篇ViewModel和LiveData)
- 谷歌官方Android应用架构库——LiveData
- 谷歌官方Android应用架构库——LiveData
- android架构组件之Room
- Android架构组件之ViewModel
- LiveData
- 初探Architecture Components之LiveData
- Android LiveData简介(一)
- Android四组件之ContentProvider架构&Demo
- Android项目架构之业务组件化
- Android项目架构之业务组件化
- Android架构之组件化方案
- Java并发编程:volatile关键字解析
- Git 基础
- 一个搜索引擎搜索出来的目录与其内容不同步的实例
- 4. The Invocation API
- 【头条】佳能再发家庭打印新品 传递“新概念打印”理念
- Android架构组件之LiveData
- iOS数据存储持久化(plist,偏好设置,归档)
- Unity打开工程时卡住的问题
- SpringMVC
- 欢迎使用CSDN-markdown编辑器
- mybatis NoSuchBeanDefinitionException 解决方案
- 【安全牛学习笔记】SQLMAP- 自动注入
- Java集合框架上机练习题
- rabbitmq的通过api实现的demo