翻译官方文档LiveData

官方文档链接: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 {
    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连一个活跃的观察者都没有时就会被调用。此时没有必要与位置服务保持连接,因为保持连接会消耗大量的电量而且没任何作用。
  • setValue(),调用此方法更新LiveData中的数据,并通知活跃的观察者发生的改变。

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

public class MyFragment extends LifecycleFragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LiveData 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 {
    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有以下好处:

  • 不会内存泄漏:既然观察者绑定到了自己的Lifecycle对象上,那么当生命周期结束时,会被自动清理。
  • 不会因为Activity停止而崩溃:如果观察者的Lifecycle不是活跃状态(比如在回退栈中),将不会收到改变的事件。
  • 始终保持数据最新:如果Lifecycle重新启动(比如,Activity从回退栈中恢复到STARTED状态),观察者将接收到最新的位置信息(除非还没有)。
  • 正确处理配置更改:如果Activity或Fragment由于配置更改(比如,旋转屏幕)而重建,观察者立刻接收到最新有效的位置信息。
  • 共享资源:可以保持单个LocationLiveData实例,只连接到系统服务一次,并且正确支持应用中所有的观察者。
  • 不需要手动处理生命周期:也许会注意到,例子中的Fragment仅在想要时观察数据,不用担心生命周期结束后,仍在观察或才开始观察。Lifecycle会自动管理所有的一切,只要Fragment在观察时提供了自己的Lifecycle。

4.LiveData转换


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

  • Transformations.map()

    适用于传入改变LiveData值的方法,并将结果向下游传递。

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

    与map()方法类似,适用于传入改变LiveData的值再封装起来的方法,并将结果向下游传递。注意,传入的方法返回的LiveData对象必须包含Lifecycle。

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

这些转换操作允许在调用链上携带传递观察者绑定的Lifecycle信息,当有观察者观察转换后的LiveData时,才真正开始执行这些转换(类似响应式编程)。这种方式允许隐式传递生命周期相关的行为,不需要添加显式的调用和依赖。任何时候,在ViewModel中需要一个Lifecycle对象,转换操作有可能解决问题。

举个例子,假设有个界面,用户输入一个地址,将收到该地址对应的邮编。

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    public MyViewModel(PostalCodeRepository repository) {
       this.repository = repository;
    }

    private LiveData 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 addressInput = new MutableLiveData();
    public final LiveData 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),实时地反馈数据的变化来驱动界面的更新。因为数据变化的发生不受界面控制,若不加入对界面生命周期的监听,容易出现操作的发生与界面的生命周期不符。

你可能感兴趣的:(翻译官方文档LiveData)