Jetpack LiveData 可被观察数据 MD

Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 [email protected]

目录

LiveData

官方文档

LiveData 简介

LiveData 是一个可被观察的数据持有者类。与常规的Observable不同,LiveData能意识到应用程序组件的生命周期变化,这意味着它能遵守Activity、Fragment、Service等组件的生命周期。这种意识确保LiveData只更新处于活跃状态的应用程序组件Observer。

如果一个Observer的生命周期处于STARTED或RESUMED状态,那么LiveData将认为这个Observer处于活跃状态。 LiveData仅通知活跃的Observer去更新UI。 非活跃状态的Observer,即使订阅了LiveData,也不会收到更新的通知。

结合一个实现了LifecycleOwner接口的对象,你能注册一个Observer。这种结合关系使得当具有生命周期的对象的状态变为DESTROYED时,Observer将被取消订阅。 这对于Activity和Fragment尤其有用,因为它们可以安全地订阅LiveData对象,而不必担心内存泄漏 - 当Activity和Fragment生命周期为DESTROYED时,它们立即会被取消订阅。

LiveData 功能概览

LiveData 也是一个观察者模型,但是它是一个与 Lifecycle 绑定了的 Subject,也就是说,只有当 UI 组件处于 ACTIVE 状态时,它的 Observer 才能收到消息,否则会自动切断订阅关系,不用再像 RxJava 那样通过 CompositeDisposable 来手动处理。

LiveData 的数据类似 EventBus 的 sticky event,不会被消费掉,只要有数据,它的 observer 就会收到通知。如果我们要把 LiveData 用作事件总线,还需要做一些定制,Github 上搜 SingleLiveEvent 可以找到源码实现。

我们没法直接修改 LiveData 的 value,因为它是不可变的(immutable),可变(mutable)版本是 MutableLiveData,通过调用 setValue(主线程)或 postValue(非主线程)可以修改它的 value。如果我们对外暴露一个 LiveData,但是不希望外部可以改变它的值,可以用如下技巧实现:

内部用 MutableLiveData ,可以修改值,对外暴露成 LiveData 类型,只能获取值,不能修改值。

LiveData 有一个实现了中介者模式的子类 —— MediatorLiveData,它可以把多个 LiveData 整合成一个,只要任何一个 LiveData 有数据变化,它的观察者就会收到消息。

综上,我们汇总一下 LiveData 的使用场景:

  • LiveData - immutable 版本
  • MutableLiveData - mutable 版本
  • MediatorLiveData - 可汇总多个数据源
  • SingleLiveEvent - 事件总线

LiveData 只存储最新的数据,虽然用法类似 RxJava2 的 Flowable,但是它不支持背压(backpressure),所以不是一个流(stream),利用 LiveDataReactiveStreams 我们可以实现 Flowable 和 LiveData 的互换。

如果把异步获取到的数据封装成 Flowable,通过 toLiveData() 方法转换成 LiveData,既利用了 RxJava 的线程模型,还消除了 Flowable 与 UI Controller 生命周期的耦合关系,借助 DataBinding 再把 LiveData 绑定到 xml UI 元素上,数据驱动 UI,妥妥的响应式。于是一幅如下模样的数据流向图就被勾勒了出来:

图中右上角的 Local Data 是 AAC 提供的另一个强大武器 —— ORM 框架 Room。

LiveData 的优点

在项目中使用LiveData,会有以下优点:

  • 确保UI符合数据状态:LiveData遵循观察者模式。 当生命周期状态改变时,LiveData会向Observer发出通知。 您可以把更新UI的代码合并在这些Observer对象中。不必去考虑导致数据变化的各个时机,每次数据有变化,Observer都会去更新UI。
  • 没有内存泄漏:Observer会绑定具有生命周期的对象,并在这个绑定的对象被销毁后自行清理。
  • 不会因停止Activity而发生崩溃:如果Observer的生命周期处于非活跃状态,例如在后退堆栈中的Activity,就不会收到任何LiveData事件的通知。
  • 不需要手动处理生命周期:UI组件只需要去观察相关数据,不需要手动去停止或恢复观察。LiveData会进行自动管理这些事情,因为在观察时,它会感知到相应组件的生命周期变化。
  • 始终保持最新的数据:如果一个对象的生命周期变到非活跃状态,它将在再次变为活跃状态时接收最新的数据。 例如,后台Activity在返回到前台后立即收到最新数据。
  • 正确应对配置更改:如果一个Activity或Fragment由于配置更改(如设备旋转)而重新创建,它会立即收到最新的可用数据。
  • 共享资源:您可以使用单例模式扩展LiveData对象并包装成系统服务,以便在应用程序中进行共享。LiveData对象一旦连接到系统服务,任何需要该资源的Observer都只需观察这个LiveData对象。

LiveData 使用步骤

按照以下步骤使用LiveData对象:

  • 创建一个LiveData的实例来保存特定类型的数据,这通常在ViewModel类中完成。
  • 创建一个定义了onChanged()方法的Observer对象,当LiveData对象保存的数据发生变化时,onChanged()方法可以进行相应的处理。您通常在UI控制器(如Activity或Fragment)中创建Observer对象。
  • 使用observe()方法将Observer对象注册到LiveData对象。observe()方法还需要一个LifecycleOwner对象作为参数。Observer对象订阅了LiveData对象,便会在数据发生变化时发出通知。您通常需要UI控制器(如Activity或Fragment)中注册Observer对象。

Note:您可以使用observeForever(Observer)方法注册一个没有关联LifecycleOwner对象的Observer。在这种情况下,Observer被认为始终处于活动状态,因此当有数据变化时总是会被通知。您可以调用removeObserver(Observer)方法移除这些Observer。

当你更新LiveData对象中存储的数据时,所有注册了的Observer,只要所绑定的LifecycleOwner处于活动状态,就会被触发通知。

创建LiveData对象
LiveData是一个包装器,可用于任何数据,包括实现Collections的对象,如List。一个LiveData对象通常存储在ViewModel对象中,并通过getter方法访问。

确保在ViewModel而不是Activity或Fragment中保存用来更新UI的LiveData对象,原因如下:

  • 避免臃肿的Activity和Fragment。这些UI控制器负责显示数据而不是保存数据状态
  • 将LiveData实例与特定Activity或Fragment实例分离,这将使得LiveData对象在配置更改后仍然存活。

观察LiveData对象
在大多数情况下,出于以下原因,应用程序组件的onCreate()方法是开始观察LiveData对象的最佳位置:

  • 确保系统不会从Activity或Fragment的onResume()方法中进行多余的调用。
  • 确保Activity或Fragment一旦变为活动状态时,就有可展示的数据。当应用程序组件处于STARTED状态,它就需从它所观察的LiveData对象中接收到最新的值。所以我们需要在一开始就设置好观察。

通常情况下,LiveData只在数据有变化时,给活跃的Observer进行通知。此行为的一个例外是,Observer在从非活跃状态变为活跃状态时也会收到通知。并且,如果Observer第二次从非活跃状态变为活跃状态,则只有在自上一次变为活跃状态以来该数据发生变化时才会接收到更新。

更新LiveData对象
LiveData没有公用的方法来更新存储的数据,MutableLiveData类暴露公用的setValue(T)和postValue(T)方法,如果需要编辑存储在LiveData对象中的值,必须使用这两个方法。

通常在ViewModel中使用MutableLiveData,然后ViewModel仅向Observer公开不可变的LiveData对象。

Note: 必须要从主线程调用setValue(T) 方法来更新LiveData 对象,如果代码在工作线程中执行, 你可以使用postValue(T) 方法来更新LiveData对象.

Transformations

您可能希望先转换存储在 LiveData 对象中的值,然后再将其分派给 Observer,或者您可能需要根据一个 LiveData 实例的值返回不同的 LiveData 实例。Lifecycle 包提供了 Transformations 类,提供了支持这些使用场景的方法。

要实现自己的转换,您可以使用MediatorLiveData类,该类监听其他LiveData对象并处理它们发出的事件。MediatorLiveData将其状态正确地传播到源LiveData对象。

map

map()使用一个函数来转换存储在LiveData对象中的值,并向下传递转换后的值。

源码

@MainThread
public static <X, Y> LiveData<Y> map(LiveData<X> source, final Function<X, Y> func) {
    final MediatorLiveData<Y> result = new MediatorLiveData<>(); //创建了一个中介者
    result.addSource(source, x -> result.setValue(func.apply(x))); //当源LiveData数据改变时通知中介,中介更改数据后通知观察者
    return result; //返回的就是中介
}

Applies the given function on the main thread to each value emitted by source LiveData and returns LiveData, which emits resulting values.

将主线程上的给定函数应用于源LiveData发出的每个值,并返回发送结果值的LiveData。

The given function func will be executed on the main thread.

给定的函数func将在主线程上执行。

使用案例

Transformations.map(MyLiveData.get(), s -> 100 + s.length()) //使用map进行数据转换
    .observe(this, i -> Log.i("bqt", "【观察到的值】" + i));

switchMap

switchMap()与map()类似,不同的是,其要求传递给switchMap()的函数必须返回一个LiveData对象

switchMap主要会进行重新绑定的操作,如果从switchMapFunction中获得的LiveData发生了变化,则会重新进行observe,从而避免上文提到的重复observe的问题

源码

@MainThread
public static <X, Y> LiveData<Y> switchMap(LiveData<X> trigger, 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, result::setValue); //添加新的源
        }
    });
    return result;
}

此机制允许较低级别的应用程序创建按需延迟计算的LiveData对象。ViewModel对象可以很容易地获得对LiveData对象的引用,然后在其上定义转换规则。

案例

Transformations.switchMap(MyLiveData.get(), s -> {
    MutableLiveData<Integer> liveData = ViewModelProviders.of(this).get(MyViewModel.class).getMutableLiveData();
    liveData.observe(this, i -> Log.i("bqt", "【观察到的值1】" + i));
    liveData.setValue(1000 + new Random().nextInt(100));
    return liveData;
})
    .observe(this, i -> Log.i("bqt", "【观察到的值2】" + i));

MediatorLiveData

我们从源码中可以看到两个方法都使用了MediatorLiveData<T>这个类型。这个类和MutableLiveData<T>一样都继承自LiveData<T>,主要是帮助我们解决这样一种case:我们有多个LiveData实例,这些LiveData的value发生变更时都需要通知某一个相同的LiveData。

我们还可以在监听value变化的过程中根据需要取消对某个LiveData的observe

LiveData subclass which may observe other LiveData objects and react on做出反应 OnChanged events from them. This class correctly正确地 propagates传播 its active/inactive states down to source LiveData objects.

使用案例

MutableLiveData 简单使用案例

LiveData which publicly exposes公开暴露 setValue(T) and postValue(T) method.
将 LiveData 的 setValue 和 postValue 访问权限修改为了 public

public class MutableLiveData<T> extends LiveData<T> 

案例一:直接在Activity中定义LiveData
这种方式不推荐

MutableLiveData<String> liveData = new MutableLiveData<>(); //定义LiveData
liveData.observe(this, s -> tvTitle.setText("值为:" + s)); //将Observer对象注册到LiveData对象
liveData.setValue(value);//修改LiveData存储的值会立即触发onChanged()回调
new Thread(() -> liveData.postValue(value)).start(); //在子线程必须使用 postValue

调用observe()之后,将立即回调onChanged(),提供存储在LiveData中的最新值。如果LiveData对象存储的值并未设置,则不调用onChanged()。
当通过setValue修改LiveData存储的值时,会立即回调onChanged()方法。
在主线程可以使用setValue也可以使用postValue,但在子线程必须使用postValue,否则报llegalStateException: Cannot invoke

案例二:和ViewModel结合使用
定义ViewModel:

public class MyViewModel extends ViewModel {
    private MutableLiveData<String> mCurrentName;

    public MutableLiveData<String> getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = new MutableLiveData<>();
        }
        return mCurrentName;
    }
}
MyViewModel mModel = ViewModelProviders.of(this).get(MyViewModel.class); //Google推荐的方式
mModel.getCurrentName().observe(this, s -> tvTitle.setText("值为:" + s));
mModel.getCurrentName().setValue(value);

案例三:和单例模式结合使用
定义LiveData:

public class MyMutableLiveData extends MutableLiveData<String> {

    private MyMutableLiveData() {
    }

    public static MyMutableLiveData get() {
        return SingleHolder.INSTANCE;
    }

    private static class SingleHolder {
        private static final MyMutableLiveData INSTANCE = new MyMutableLiveData();
    }
}
MyMutableLiveData.get().observe(this, s -> tvTitle.setText("值为:" + s));
MyMutableLiveData.get().setValue(value);

扩展 LiveData

LiveData

public class MyLiveData extends LiveData<String> {
    private Manager manager;
    private OnChangeListener listener = time -> postValue("回调值:" + time); //因为在子线程中,所以一定要用postValue

    private MyLiveData() {
        manager = new Manager();
    }

    public static MyLiveData get() {
        return SingleHolder.INSTANCE;
    }

    private static class SingleHolder {
        private static final MyLiveData INSTANCE = new MyLiveData();
    }

    @Override
    protected void onActive() {
        super.onActive();
        Log.i("bqt", "【onActive】");
        manager.addListerner(listener);
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        Log.i("bqt", "【onInactive】");
        manager.removeListerner(listener);
    }
}

Manager

public class Manager {
    private boolean stop = false;
    private Set<OnChangeListener> listeners = new HashSet<>();

    public Manager() {
        new Thread(() -> {
            while (!stop) {
                SystemClock.sleep(2000);
                for (OnChangeListener listener : listeners) {
                    String time = new SimpleDateFormat("HH:mm:ss SSS", Locale.getDefault()).format(new Date());
                    listener.onChanged(time);
                }
            }
        }).start();
    }

    public void addListerner(OnChangeListener listener) {
        listeners.add(listener);
    }

    public void removeListerner(OnChangeListener listener) {
        listeners.remove(listener);
    }

    public void stop() {
        stop = true;
    }
}

案例说明

回调接口

public interface OnChangeListener {
    void onChanged(String time);
}

本例中LiveData的实现包括以下重要的方法:

  • 当LiveData对象有一个活跃的Observer时,onActive()方法被调用,这意味着你需要从这个方法开始观察Manager的更新。
  • 当LiveData对象没有任何活跃的Observer时,onInactive()方法被调用,由于没有Observer在监听,所以没有理由继续保持与Manager的连接。
  • setValue(T)方法更新LiveData实例的值,并通知活动观察者有关更改。
MyLiveData.get().observe(this, s -> Log.i("bqt", "【观察到的值】" + s));

observe()方法第一个参数是作为LifecycleOwner实例的Fragment或FragmentActivity,这样做表示此Observer绑定了Lifecycle对象的生命周期,即:

  • 如果Lifecycle对象不处于活动状态,则即使值发生更改,也不会调用Observer。
  • Lifecycle对象被销毁后,Observer被自动删除。
  • LiveData对象具有感知生命周期的能力,意味着您可以在多个Activity,Fragment和service之间共享它们。

MediatorLiveData 案例

public class MyMediatorLiveData extends MediatorLiveData<String> {
    @Override
    protected void onActive() {
        super.onActive();
        Log.i("bqt", "【onActive】");
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        Log.i("bqt", "【onInactive】");
    }
}

添加源

MyMediatorLiveData mediatorLiveData = new MyMediatorLiveData();
mediatorLiveData.addSource(liveData1, s -> mediatorLiveData.setValue("liveData1-" + s));
mediatorLiveData.addSource(liveData2, s -> mediatorLiveData.setValue("liveData2-" + s));
mediatorLiveData.observe(this, s -> tvTitle.setText("值为:" + s));

在源LiveData数据变更后MediatorLiveData注册的观察者就会收到通知。

liveData1.postValue(value1);
liveData2.setValue(value2);

2019-3-29

猜你喜欢

转载自www.cnblogs.com/baiqiantao/p/10621008.html
今日推荐