android LiveData

来源:互联网 发布:windows 10 ltsb iso 编辑:程序博客网 时间:2024/06/05 08:06

LiveData是数据holder类,并支持数据可被监听(观察)。和传统的观察者模式中的被观察者不一样,LiveData是一个生命周期感知组件,因此观察者可以指定某一个LifeCycle给LiveData,并对数据进行监听。

如果观察者的生命周期处于STARTED or 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);    }}

在上面这个例子实现中有三个重要的部分:

onActive()
当LiveData有一个活动的观察者时,就会调用这个方法。这意味着我们需要开始观察设备的位置更新。

onInactive()
当LiveData没有任何活动的观察者时,就会调用这个方法。由于没有观察人员在监听,所以没有理由与LocationManager服务保持联系。这一点很重要,因为保持连接会消耗大量的电池,而没有任何好处。

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作为第一个参数传递了进去,这样做意味着这个观察者应该被绑定到这个生命周期

如果LifeCycle不在Started或者RESUMED这两个状态,那么观察者将无法接受到数据更新的回调,即使数据发生了变化。

如果LifeCycle销毁了,即生命周期结束,观察者将被自动从LiveData中移除。

既然LocationLiveData是生命周期感知的,我们可以在多个activity、fragment之间共享它,让它可以被多个Activity或者Fragment公用:

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) {        Util.checkUserStatus(result -> {            if (result) {                MyLocationListener.get(getActivity()).addObserver(this, location -> {                   // update UI                });            }        });  }}

这样即使有多个Activity或者Fragment在观察MyLocationListener实例,LiveData会优雅地管理它们,如果它们中的任何一个都是可见的(也就是活动的),它只会连接到系统服务。

下面是LiveData类提供的一些优点:

没有内存溢出
当观察者被绑定他们对应的LifeCycle以后,当页面销毁时他们会自动被溢出,不会导致内存溢出。

不会因为Activity的不可见导致Crash
当Activity不可见时,即使有数据变化,LiveData也不会通知观察者。因为此时观察者的LifeCyele并不处于Started或者RESUMED状态。

配置的改变
当当前Activity配置改变(如屏幕方向),导致重新从onCreate走一遍,这是观察者们会立刻收到配置变化前的最新数据。

资源共享
我们只需要一个LocationLivaData,连接系统服务一次,就能支持所有的观察者。

不再手动为生命周期处理
通过上面的代码可以知道,我们的Activity或者Fragment只要在需要观察数据的时候观察数据即可,不需要理会生命周期变化了。这一切都交给LiveData来自动管理。

LiveData的转换

有时,您可能希望在将LiveData值发送到观察者之前对LiveData值进行更改,或者您可能需要根据另一个值 返回一个不同的LiveData实例。

生命周期包提供了一个Transformations类,它可以帮助完成上面的这些操作。

Transformations.map()

在LiveData数据传递之前,改变数据后再传递:

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

与Transformations.map()类似,只不过这里传递个switchMap()的方法必须返回一个LiveData对象。

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

当你考虑在ViewModel中使用LifeCycle对象时,这种转换就是一个可选的解决方案。
假如有一下需求,用户输入一个地址,我们在屏幕上更新这个地址对应的邮编,简单的写法如下:

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);    }}

这样写问题显然很严重,当每次调用getPostalCode方法后,UI代码中都需要对getPostalCode的返回值做注册观察者操作,并且还要移除上一个观察者,这样显然是低效率的。此外,如果这时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 final,因为它将始终不变,UI只要在需要用的时候将观察者注册到postalCode中就行。这是当用户调用setInput后,如果postalCode上有可活动的观察者,那么repository.getPostCode(address)就会被调用,如果此时没有可活动的观察者,则repository.getPostCode(address)不会被调用。

自定义转换
有多个不同的特定的转换在应用程序中可能是有用的,但他们不是默认情况下提供的。实现自己的转换可以使用MediatorLiveData类,这是专门用来发出正确听其他LiveData实例和处理事件。MediatorLiveData需要关心正确传播源LiveData主动/不活跃的状态。你可以转换类的实现细节。

交流QQ群:196040873
点击链接加入群【Android那点事】