Android开发Android知识Android技术知识

翻译官方文档LiveData

2017-09-20  本文已影响0人  lanceJin

官方文档链接:https://developer.android.google.cn/topic/libraries/architecture/livedata.html

1.前言


前面讲到了AAC框架对于生命周期的支持,这一篇来看看框架自己提供的可感知生命周期的类。除了学会怎么使用它,还能增加对框架的核心Lifecycle的认识。

2.使用案例


LiveData这个类持有数据并允许数据被观察。不同于普通的被观察者,它可以被指定组件的Lifecycle,按照该生命周期提供观察的通知。LiveData认为,只有当观察者绑定的Lifecycle.State处于STARTED或者RESUMED时,观察者才是活跃的(可以发送通知)。

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

上面实现位置服务的监听有三个需注意的地方:

LocationLiveData可以按照以下方式使用:

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

observe()方法传入LifecycleOwner对象作为第一个参数,意味着之后传入的观察者将绑定到Lifecycle上:

  • 如果Lifecycle不在活跃状态(STARTED或RESUMED),当值发生改变后,观察者并不会收到通知。
  • 如果Lifecycle被销毁了,观察者将自动从观察队列中移除。

3.生命周期感知


实际上,LiveData的生命周期感知特性提供了新的选择:在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) {
                LocationLiveData.get(getActivity()).observe(this, location -> {
                   // update UI
                });
            }
        });
  }
}

即使有多个Activity和Fragment在观察LocationLiveData对象,但仍能做到规范地管理,比如,至少有一个观察者是活跃的,才会连接到系统服务。所以,使用LiveData有以下好处:

4.LiveData转换


有时候,希望在给观察者分发数据之前改变LiveData的值,或者根据不同的值返回不同的LiveData对象。Lifecycle包提供了Transformations类,包含有助于执行这些操作的方法。

这些转换操作允许在调用链上携带传递观察者绑定的Lifecycle信息,当有观察者观察转换后的LiveData时,才真正开始执行这些转换(类似响应式编程)。这种方式允许隐式传递生命周期相关的行为,不需要添加显式的调用和依赖。任何时候,在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()方法时,先从之前的LiveData对象中注销观察者,再注册到新的LiveData对象中。此外,当界面重建时,将会重新调用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修饰。定义了基于addressInput对象的转换,当它发生改变且有活跃的观察者时,将会调用getPostalCode()方法。若当时没有活跃的观察者,不会执行任何转换直到加入一个观察者。

这种机制允许在应用底层创建LiveData对象,并满足响应式地运行。而ViewModel能容易地获取它们,并基于它们定义转换规则。

5.创建新的转换


应用可能需要十几种不同类型地特定转换,但是默认没有提供。可以使用MediatorLiveData类来实现自己所需的转换,因为它是为了正确监听其它LiveData实例,并处理它们发出的事件而专门创建的类。MediatorLiveData关注于正确传递活跃/非活跃状态给原始的LiveData。

6.总结


LiveData目的就是为了控制反转(IOC),实时地反馈数据的变化来驱动界面的更新。因为数据变化的发生不受界面控制,若不加入对界面生命周期的监听,容易出现操作的发生与界面的生命周期不符。

上一篇下一篇

猜你喜欢

热点阅读