LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.
LiveData是一种持有可被观察数据的类。和其他可被观察的类不同的是,LiveData是有生命周期感知能力的,这意味着它可以在activities,fragments,或者services生命周期是活跃状态时更新这些组件。那么什么是活跃状态呢?上篇文章中提到的STARTED和RESUMED就是活跃状态,只有在这两个状态下LiveData是会通知数据变化的。
要想使用LiveData(或者这种有可被观察数据能力的类)就必须配合实现了LifecycleOwner的对象使用。在这种情况下,当对应的生命周期对象DESTROYED时,才能移除观察者。这对Activity或者Fragment来说显得尤为重要,因为他们可以在生命周期结束的时候立刻解除对数据的订阅,从而避免内存泄漏等问题。
使用LiveData的优点:
UI和实时数据保持一致 因为LiveData采用的是观察者模式,这样一来就可以在数据发生改变时获得通知,更新UI。- 避免内存泄漏,观察者被绑定到组件的生命周期上,当被绑定的组件销毁(
destory)时,观察者会立刻自动清理自身的数据。 - 不会再产生由于
Activity处于stop状态而引起的崩溃 例如:当Activity处于后台状态时,是不会收到LiveData的任何事件的。 - 不需要再解决生命周期带来的问题
LiveData可以感知被绑定的组件的生命周期,只有在活跃状态才会通知数据变化。 - 实时数据刷新,当组件处于活跃状态或者从不活跃状态到活跃状态时总是能收到最新的数据
- 解决
Configuration Change问题,在屏幕发生旋转或者被回收再次启动,立刻就能收到最新的数据。 - 数据共享,如果对应的
LiveData是单例的话,就能在app的组件间分享数据。
使用LiveData:
- 创建一个持有某种数据类型的
LiveData(通常是在ViewModel中) - 创建一个定义了
onChange()方法的观察者。这个方法是控制LiveData中数据发生变化时,采取什么措施 (比如更新界面)。通常是在UI Controller(Activity/Fragment)中创建这个观察者。 - 通过
observe()方法连接观察者和LiveData。observe()方法需要携带一个LifecycleOwner类。这样就可以让观察者订阅LiveData中的数据,实现实时更新。
LiveData是一个数据的包装。具体的包装对象可以是任何数据,包括集合(比如List)。LiveData通常在ViewModel中创建,然后通过getter方法获取。具体可以看一下代码:
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...
}通常情况下都是在组件的onCreate()方法中开始观察数据,原因有以下两点:
- 系统会多次调用
onResume()方法 - 确保
Activity/Fragment在处于活跃状态时立刻可以展示数据。
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);
}
}如果想要在UI Controller中改变LiveData中的值呢?(比如点击某个Button把性别从男设置成女)。LiveData并没有提供这样的功能,但是Architecture Component提供了MutableLiveData这样一个类,可以通过setValue(T)和postValue(T)方法来修改存储在LiveData中的数据。MutableLiveData是LiveData的一个子类,从名称上也能看出这个类的作用。举个直观点的例子:
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "John Doe";
mModel.getCurrentName().setValue(anotherName);
}
})调用setValue()方法就可以把LiveData中的值改为John Doe。同样通过这种方法修改LiveData中的值同样会触发所有对这个数据感兴趣的类。那么setValue()和postValue()有什么不同呢?区别就是setValue()只能在主线程中调用,而postValue()可以在子线程中调用。
Room可以返回LiveData的数据类型。这样对数据库中的任何改动都会被传递出去。这样修改完数据库就能获取最新的数据,减少了主动获取数据的代码。
LiveData的活跃状态包括:STARTED或者RESUMED两种状态。那么如何在活跃状态下把数据传递出去呢?下面是示例代码:
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);
}
}上面有三个重要的方法:
- The onActive() method is called when the LiveData object has an active observer. This means you need to start observing the stock price updates from this method.
- The onInactive() method is called when the LiveData object doesn't have any active observers. Since no observers are listening, there is no reason to stay connected to the StockManager service.
- The setValue(T) method updates the value of the LiveData instance and notifies any active observers about the change.
可以像下面这样使用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.
});
}
}上面observe()方法中的第一个参数传递的是fragment的实例,该fragment实现了LifecycleOwner接口。这样做是为了将observer和Lifecycle对象绑定到一起,这意味着:
- 如果当前的
Lifecycle对象不是出于活跃期,就算value值有改变也不会回调到observer中 - 在
Lifecycle对象销毁后哦,observer对象也会自动移除
实际上LiveData对象是适应生命周期也就意味着你需要在多个activities,fragments和services中进行共享,所以通常我们会将LiveData的示例设计成单例的:
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);
}
}这样就可以在fragment中像如下这样使用:
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
StockLiveData.get(getActivity()).observe(this, price -> {
// Update the UI.
});
}
}你可能有时会在LiveData分发给observers之前想要修改一下存储在LiveData中的值,或者你想根据当前的值进行修改返回另一个值。Lifecycle提供了Transformations类来通过里面的helper方法解决这种问题。
Transformations.map()
可以将LiveData中的数据进行改变。
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});将LiveData中的User数据转换成String
Transformations.switchMap()
private LiveData<User> getUser(String id) {
...;
}
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );和上面的map()方法很像。区别在于传递给switchMap()的函数必须返回LiveData对象。
和LiveData一样,Transformation也可以在观察者的整个生命周期中存在。只有在观察者处于观察LiveData状态时,Transformation才会运算。Transformation是延迟运算的(calculated lazily),而生命周期感知的能力确保不会因为延迟发生任何问题。
如果在ViewModel对象的内部需要一个Lifecycle对象,那么使用Transformation是一个不错的方法。举个例子:假如有个UI组件接受输入的地址,返回对应的邮政编码。那么可以 实现一个ViewModel和这个组件绑定:
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
public MyViewModel(PostalCodeRepository repository) {
this.repository = repository;
}
private LiveData<String> getPostalCode(String address) {
// DON'T DO THIS
return repository.getPostCode(address);
}
}看代码中的注释,有个// DON'T DO THIS(不要这么干),这是为什么?有一种情况是如果UI组件被回收后又被重新创建,那么又会触发一次repository.getPostCode(address)询,而不是重用上次已经获取到的查询。那么应该怎样避免这个问题呢?看一下下面的代码:
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
private final MutableLiveData<String> addressInput = new MutableLiveData();
public final LiveData<String> postalCode =
Transformations.switchMap(addressInput, (address) -> {
return repository.getPostCode(address);
});
public MyViewModel(PostalCodeRepository repository) {
this.repository = repository
}
private void setInput(String address) {
addressInput.setValue(address);
}
}postalCode变量的修饰符是public和final,因为这个变量的是不会改变的。哎?不会改变?那我输入不同的地址还总返回相同邮编?先打住,postalCode这个变量存在的作用是把输入的addressInput转换成邮编,那么只有在输入变化时才会调用repository.getPostCode()方法。这就好比你用final来修饰一个数组,虽然这个变量不能再指向其他数组,但是数组里面的内容是可以被修改的。绕来绕去就一点:当输入是相同的情况下,用了switchMap()可以减少没有必要的请求。并且同样,只有在观察者处于活跃状态时才会运算并将结果通知观察者。
MediatorLiveData是LiveData的子类,可以通过MediatorLiveData合并多个LiveData来源的数据。同样任意一个来源的LiveData数据发生变化,MediatorLiveData都会通知观察他的对象。说的有点抽象,举个例子。比如UI接收来自本地数据库和网络数据,并更新相应的UI。可以把下面两个LiveData加入到MeidatorLiveData中:
- 关联数据库的
LiveData - 关联联网请求的
LiveData相应的UI只需要关注MediatorLiveData就可以在任意数据来源更新时收到通知。
- 邮箱 :charon.chui@gmail.com
- Good Luck! `