LiveData是一个可以被观察的数据持有类,它可以感知并遵循Activity、Fragment或Service等组件的生命周期。正是由于LiveData对组件生命周期可感知特点,因此可以做到仅在组件处于生命周期的激活状态时才更新UI数据。
LiveData需要一个观察者对象,一般是Observer
类的具体实现。当观察者的生命周期处于STARTED
或RESUMED
状态时,LiveData会通知观察者数据变化;在观察者处于其他状态时,即使LiveData的数据变化了,也不会通知。
LiveData可以使用如下方法注册观察者:
@MainThread
public 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 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而不用担心造成内存泄露。
Observer
跟LifecycleOwner
建立关系,从而可以获取生命周期状态,当组件生命周期状态为DESTROYED
时,会移除观察者。使用LiveData的基本步骤:
Observer
对象并实现其onChanged()
回调,一般在Activity或Fragment中创建Observer
。observe(@NonNull LifecycleOwner owner, @NonNull Observer observer)
方法建立观察关系。LiveData的
observeForever(@NonNull Observer
方法不需要传入observer) LifecycleOwner
,这意味着是永久观察,无关生命周期状态,任何时候有数据更新都会回调onChanged()
。
public class NameViewModel extends ViewModel {
// Create a LiveData with a String
private MutableLiveData<String> mCurrentName;
public MutableLiveData<String> getCurrentName() {
if (mCurrentName == null) {
mCurrentName = new MutableLiveData<String>();
}
return mCurrentName;
}
// Rest of the ViewModel...
}
一般LiveData都会在ViewModel中实现,MutableLiveData继承自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没有对外提供公有的修改数据的方法,而其子类MutableLiveData
提供了setValue(T)
(主线程使用)和postValue(T)
(子线程使用)两个方法允许修改数据。MutableLiveData
源码定义如下:
/**
* {@link LiveData} which publicly exposes {@link #setValue(T)} and {@link #postValue(T)} method.
*
* @param 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。
更多关于Room的用法可参考 Room persistent library guide。
通常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.
*
* 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.
*
* 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).
*
* 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);
}
}
在实际应用中可能有这样一些场景:
支持库提供了一个Transformations
类,并提供了两个现成的变换方法可以帮助我们实现上述需求。
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.
*
* The given function {@code func} will be executed on the main thread.
*
* 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 a type of {@code source} LiveData
* @param a type of resulting LiveData.
* @return a LiveData which emits resulting values
*/
@MainThread
public 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
});
Transformations.switchMap()
对应源码为:
/*
* @param trigger a {@code LiveData} to listen to
* @param func a function which creates "backing" LiveData
* @param a type of {@code source} LiveData
* @param a type of resulting LiveData
*/
@MainThread
public 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。
MediatorLiveData
类可以合并多个LiveData数据源,只要任何一个LiveData数据发生变化,MediatorLiveData
的观察者都会收到通知。
示例场景:
一个Activity界面的UI数据依赖于网络数据源和数据库数据,因此会有两个LiveData。使用MediatorLiveData
将两个LiveData合并后,Activity只需要观察一个MediatorLiveData
即可。无论什么时候,只要任何一个LiveData数据源发生变化,都会通知Activity的UI进行更新。
参考:
https://developer.android.google.cn/topic/libraries/architecture/livedata.html