翻译官方文档LiveData

作者: lanceJin | 来源:发表于2017-09-20 22:50 被阅读0次

    官方文档链接: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);
        }
    }
    

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

    • onActive(),当LiveData有一个活跃的观察者时就会被调用。可以在这里开始观察设备位置的更新。
    • onInactive(),当LiveData连一个活跃的观察者都没有时就会被调用。此时没有必要与位置服务保持连接,因为保持连接会消耗大量的电量而且没任何作用。
    • setValue(),调用此方法更新LiveData中的数据,并通知活跃的观察者发生的改变。

    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有以下好处:

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

    4.LiveData转换


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

    • Transformations.map()

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

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

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

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

    相关文章

      网友评论

        本文标题:翻译官方文档LiveData

        本文链接:https://www.haomeiwen.com/subject/msjasxtx.html