Android架构组件之LiveData

来源:互联网 发布:编程之魂 译者 编辑:程序博客网 时间:2024/05/21 05:04

基本概念

LiveData是一个可以被观察的数据持有类,它可以感知并遵循Activity、Fragment或Service等组件的生命周期。正是由于LiveData对组件生命周期可感知特点,因此可以做到仅在组件处于生命周期的激活状态时才更新UI数据。

LiveData需要一个观察者对象,一般是Observer类的具体实现。当观察者的生命周期处于STARTEDRESUMED状态时,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的优点

  1. 确保数据源跟UI展示一致——当数据源变化时,LiveData会通知观察者更新UI,前提是组件在激活状态下。
  2. 不会造成内存泄露——由于ObserverLifecycleOwner建立关系,从而可以获取生命周期状态,当组件生命周期状态为DESTROYED时,会移除观察者。
  3. 当组件处于非激活状态时,不会收到数据更新回调。
  4. 无需手动处理生命周期——UI组件只需要观察对应的数据,LiveData根据其生命周期自动处理。
  5. 总是最新的数据——当Activity从后台切到前台时,总会收到最新的数据。
  6. 适配设备配置变化——如屏幕旋转,组件销毁重建后,立即收到上次的数据。
  7. 资源共享——参考【扩展LiveData】一节。

LiveData使用方法

使用LiveData的基本步骤:

  1. 在ViewModel中创建LiveData,并持有某种类型的数据。
  2. 创建一个Observer对象并实现其onChanged()回调,一般在Activity或Fragment中创建Observer
  3. 在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使用

  1. Room 是Android官方的持久化数据库方案,支持返回LiveData对象,当数据库变化时会更新LiveData,这些逻辑由Room自动生成。
  2. 上述逻辑是在后台异步进行的,这种模式可以使UI跟数据库的数据保持同步。

更多关于Room的用法可参考 Room persistent library guide。

扩展LiveData

通常LiveData只关心观察者的STARTEDRESUMED状态,下面代码片段扩展了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变换

在实际应用中可能有这样一些场景:

  1. 希望修改LiveData中的数据,然后将修改后的结果发送到观察者。
  2. 希望基于当前的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

原创粉丝点击