本文将介绍Architecture Components中的LiveData组件

LiveData

LiveData 一个可被观察的数据容器。 和其他observable不一样的是, 观察者Observer 可以指定一个有 Lifecycle的app component。

只会在ObserverLifecycleSTARTED 或者 RESUMED时,LiveData 才认为这是一个有效的观察者.

public class LocationLiveData extends LiveData<Location> {
private LocationManager locationManager;

private SimpleLocationListener listener = new SimpleLocationListener() {
@Override
public void onLocationChanged(Location location) {
setValue(location);
}
};

public LocationLiveData(Context context) {
locationManager = (LocationManager) context.getSystemService(
Context.LOCATION_SERVICE);
}

@Override
protected void onActive() {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}

@Override
protected void onInactive() {
locationManager.removeUpdates(listener);
}
}

Location listener中有三个值得关注的地方

  • onActive()

LiveData有一个处于活动状态的观察者时被调用。这意味着我们开始观察location数据了。

  • onInactive()

LiveData没有任何活动的观察者时被调用。 在这里释放LocationManager service 可以节约电量.

  • setValue()

LiveData的数据更新时被调用,这里会通知所有的观察者数据发生了改变。

可以这样使用LocationLiveData:

public class MyFragment extends LifecycleFragment {
public void onActivityCreated (Bundle savedInstanceState) {
LiveData<Location> myLocationListener = ...;
Util.checkUserStatus(result -> {
if (result) {
myLocationListener.addObserver(this, location -> {
// update UI
});
}
});
}
}

注意 addObserver() 第一个参数LifecycleOwner。 observer 和 Lifecycle发生了绑定:

  • Lifecycle非活动状态时(非STARTEDRESUMED), observer不会得到数据变更的通知。
  • Lifecycle destroyed后,observer自动被移除。

事实上 LiveData可以做到跨组件,不同组件可以共享同一个LiveData。这里为了简单,我们把它变成单例。

public class LocationLiveData extends LiveData<Location> {
private static LocationLiveData sInstance;
private LocationManager locationManager;

@MainThread
public static LocationLiveData get(Context context) {
if (sInstance == null) {
sInstance = new LocationLiveData(context.getApplicationContext());
}
return sInstance;
}

private SimpleLocationListener listener = new SimpleLocationListener() {
@Override
public void onLocationChanged(Location location) {
setValue(location);
}
};

private LocationLiveData(Context context) {
locationManager = (LocationManager) context.getSystemService(
Context.LOCATION_SERVICE);
}

@Override
protected void onActive() {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}

@Override
protected void onInactive() {
locationManager.removeUpdates(listener);
}
}

在 fragment 中修改如下:

public class MyFragment extends LifecycleFragment {
public void onActivityCreated (Bundle savedInstanceState) {
- LiveData<Location> myLocationListener = ...;
Util.checkUserStatus(result -> {
if (result) {
- myLocationListener.addObserver(this, location -> {
+ LocationLiveData.get(getActivity()).observe(this, location -> {
// update UI
});
// update UI
});
}
});
}
}

可能有多个 fragments 或者 activities 在观察 MyLocationListener, 而优雅的LiveData只会在这些组件处于active时连接到system service .

LiveData优点如下:

  • 再也没有内存泄露: Observer 和拥有Lifecycle的对象绑定, 当Lifecycle destroyed时,Observer会被自动释放。
  • 再也没有由 stopped activities 引起的crash:Lifecycle处于inactive 状态时(比如Activity处于back stack), Observer不会收到数据变更事件。
  • 总是第一时间获取最新数据: 如果Lifecycle重新start (比如activity 从 back stack重新start) ,Observer会收到最新的数据(if it didn’t already).
  • 自动处理configuration change: 如果activity 或者 fragment 因为configuration change (旋转屏幕) 而重建, Observer会立刻收到最新的数据.
  • 资源共享: 通过创建 MyLocationListener单例,system service 只需获取一次, 可以供app中的所有观察者使用。
  • 再也没有手动的生命周期管理了: 例子中的fragment 仅仅 observes 数据源即可,不需要关心自己被stop或重启后重新建立观察。 LiveData会自动处理这些。

Transformations of LiveData

有时,你可能需要在LiveData dispatching 变化的value之前加工一下数据,或者返回一个基于另外数据的 LiveData
(说的有点复杂,其实就是类似Rxjava中的map、flatmap)

通过Transformations类,我们可以实现这些操作.

  • Transformations.map()
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
  • Transformations.switchMap()
private LiveData<User> getUser(String id) {
...;
}

LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

通过transformations 你可以链式的把观察者的生命周期状态一层层传递,直到有观察者观察为止,transformations 操作才开始计算。这种懒延迟的天性可以让我们将非显式的生命周期相关联的操作传递下去而不用添加额为的依赖。

当你觉得需要在ViewModel中依赖一个Lifecycle时,使用transformation很可能就是解决方案。

举个例子:

用户在UI界面上输入地址,ViewModel返回邮编,原始的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);
}
}

为啥不要这么做呢,因为每次UI调用 getPostalCode()都要先unregister 之前的LiveData,然后重新re-register新的LiveData实例。而且每次UI重建都会触发getPostalCode(),而不是之前查询结果的缓存值。(没看懂)

改进如下:

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 finaladdressInput transformation, 它本身不会改变。如果 addressInput 发生变化, 只有当存在有效的observer时,repository.getPostCode() 才会被调用。如果没有observer,就不会产生任何额外的计算开销。

这个机制允许应用的底层创建按需加载的
LiveDataViewModel可以在Livedata层的基础上定义各种transformation规则获取数据。

Creating new transformations

可以使用 MediatorLiveData 来创建自定义 transformations,MediatorLiveData可以用来监听 LiveData实例并且处理LiveData派发的事件,MediatorLiveData会正确的处理自己的状态。更多细节可以查看Transformations.