一、简介
LiveData 是 Jitpack 组件之一,使用了观察者模式。当数据发送变化时,通知观察者。
二、API
1.导入 Lifecycle 库
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
2.LiveData 类
LiveData 类是一个抽象类,所以无法直接创建。平时我们主要使用它的两个子类:MutableLiveData、MediatorLiveData。
LiveData 有以下 public 方法:
T getValue()
获取 LiveData 的值observe(LifecycleOwner owner, Observer<? super T> observer)
添加观察者,绑定 Lifecycle 生命周期,当 Lifecycle 处于 STARTED 或者 RESUMED 时,认为此观察者是 active 的,否则认为此观察者是 inactive 的。只有处于 active 状态的观察者才会接收消息。
当 Lifecycle 状态变为 DESTROYED 时,此观察者会被自动移除,所以我们不必担心内存泄漏。observeForever(Observer<? super T> observer)
添加观察者,不绑定生命周期。这样的观察者需要我们调用 removeObserver 手动移除,这样的观察者若没有手动移除,会导致内存泄漏。removeObserver(Observer<? super T> observer)
移除观察者removeObservers(LifecycleOwner owner)
移除绑定此 Lifecycle 的所有观察者boolean hasActiveObservers()
是否有 active 的 observersboolean hasObservers()
是否有 observers,无论 active 还是 inactive
LiveData 有以下 protected 方法:
void onActive()
当观察者数量从 0 变成 1 时,回调此方法void onInactive()
当观察者数量从 1 变成 0 时,回调此方法void setValue(T value)
设置 LiveData 的值,不允许在子线程中调用void postValue(T value)
post 一个 task 到主线程中,设置 LiveData 的值,所以可以在子线程中调用
如果我们需要这些 protected 方法,可以自定义类继承 LiveData 并重载他们。
3.MutableLiveData 类
MutableLiveData 类是平时使用最多的类,源码如下:
public class MutableLiveData<T> extends LiveData<T> {
/**
* Creates a MutableLiveData initialized with the given {@code value}.
*
* @param value initial value
*/
public MutableLiveData(T value) {
super(value);
}
/**
* Creates a MutableLiveData with no value assigned to it.
*/
public MutableLiveData() {
super();
}
@Override
public void postValue(T value) {
super.postValue(value);
}
@Override
public void setValue(T value) {
super.setValue(value);
}
}
观察它的源码我们发现,它相对于 LiveData 的区别就是:它不是抽象类,并且公开了 postValue、setValue 两个方法。使得我们可以调用这两个方法修改它的值。
测试
修改布局文件 activity_main:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="+1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv" />
</androidx.constraintlayout.widget.ConstraintLayout>
修改 MainActivity:
class MainActivity : AppCompatActivity() {
// 初始化 MutableLiveData
private val data: MutableLiveData<Int> = MutableLiveData(0)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 开始观察
data.observe(this, Observer {
// 当数据改变时,设置到 TextView 上,初始值也会触发这里
tv.text = it.toString()
})
btn.setOnClickListener {
// 点击按钮时,数据 +1
data.value = data.value?.plus(1)
}
}
}
运行程序,显示如下:
如果我们修改一下 MainActivity 中点击事件的代码,点击按钮时,启动一个新线程修改 data 的值:
btn.setOnClickListener {
Thread(Runnable {
data.value = data.value?.plus(1)
}).start()
}
运行时我们会收到以下 Crash:
java.lang.IllegalStateException: Cannot invoke setValue on a background thread
这时,只要将其修改为调用 postValue 方法即可:
btn.setOnClickListener {
Thread(Runnable {
data.postValue(data.value?.plus(1))
}).start()
}
4.MediatorLiveData 类
MediatorLiveData 用来合并多个 LiveData 数据。设想一个场景,我们有两个 LiveData 数据,任何一个数据改变时,都要更新同一个 UI。此时我们就可以用 MediatorLiveData 将其合并。
相比 LiveData,MediatorLiveData 新增了两个公开方法:
addSource(LiveData<S> source, Observer<? super S> onChanged)
开始观察此 LiveData,当 source 参数的值改变时,会回调这个 onChanged 函数removeSource(LiveData<S> toRemote)
移除一个 LiveData 类,停止观察此 LiveData
MediatorLiveData 的源码如下:
@SuppressWarnings("WeakerAccess")
public class MediatorLiveData<T> extends MutableLiveData<T> {
private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();
/**
* Starts to listen the given {@code source} LiveData, {@code onChanged} observer will be called
* when {@code source} value was changed.
* <p>
* {@code onChanged} callback will be called only when this {@code MediatorLiveData} is active.
* <p> If the given LiveData is already added as a source but with a different Observer,
* {@link IllegalArgumentException} will be thrown.
*
* @param source the {@code LiveData} to listen to
* @param onChanged The observer that will receive the events
* @param <S> The type of data hold by {@code source} LiveData
*/
@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
Source<S> e = new Source<>(source, onChanged);
Source<?> existing = mSources.putIfAbsent(source, e);
if (existing != null && existing.mObserver != onChanged) {
throw new IllegalArgumentException(
"This source was already added with the different observer");
}
if (existing != null) {
return;
}
if (hasActiveObservers()) {
e.plug();
}
}
/**
* Stops to listen the given {@code LiveData}.
*
* @param toRemote {@code LiveData} to stop to listen
* @param <S> the type of data hold by {@code source} LiveData
*/
@MainThread
public <S> void removeSource(@NonNull LiveData<S> toRemote) {
Source<?> source = mSources.remove(toRemote);
if (source != null) {
source.unplug();
}
}
@CallSuper
@Override
protected void onActive() {
for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
source.getValue().plug();
}
}
@CallSuper
@Override
protected void onInactive() {
for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
source.getValue().unplug();
}
}
private static class Source<V> implements Observer<V> {
final LiveData<V> mLiveData;
final Observer<? super V> mObserver;
int mVersion = START_VERSION;
Source(LiveData<V> liveData, final Observer<? super V> observer) {
mLiveData = liveData;
mObserver = observer;
}
void plug() {
mLiveData.observeForever(this);
}
void unplug() {
mLiveData.removeObserver(this);
}
@Override
public void onChanged(@Nullable V v) {
if (mVersion != mLiveData.getVersion()) {
mVersion = mLiveData.getVersion();
mObserver.onChanged(v);
}
}
}
}
查看源码我们可以发现,MediatorLiveData 是通过给传入的 LiveData 新增观察者实现的合并,仍然是使用的 observeForever 和 removeObserver 方法,只是帮我们做了一层简单的封装而已。
测试
修改 activity_main 布局文件:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="LiveData1: +1"
android:textAllCaps="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv" />
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="LiveData2: +2"
android:textAllCaps="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn1" />
</androidx.constraintlayout.widget.ConstraintLayout>
修改 MainActivity:
class MainActivity : AppCompatActivity() {
private val liveData1: MutableLiveData<Int> = MutableLiveData(0)
private val liveData2: MutableLiveData<Int> = MutableLiveData(0)
private val liveDataMerger: MediatorLiveData<Int> = MediatorLiveData<Int>().apply {
this.value = 0
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
liveDataMerger.addSource(liveData1) {
liveDataMerger.value = it
// 红色表示此时为 LiveData1
tv.setTextColor(Color.RED)
}
liveDataMerger.addSource(liveData2) {
liveDataMerger.value = it
// 绿字表示此时为 LiveData2
tv.setTextColor(Color.GREEN)
}
liveDataMerger.observe(this, Observer {
tv.text = it.toString()
})
btn1.setOnClickListener {
liveData1.value = liveData1.value?.plus(1)
}
btn2.setOnClickListener {
liveData2.value = liveData2.value?.plus(2)
}
}
}
运行程序,显示如下: