[译] Architecture Components 之 LiveData

栏目: 后端 · 发布时间: 6年前

内容简介:[译] Architecture Components 之 LiveData

LiveData

LiveData 是一个数据持有者类,它持有一个值并允许观察该值。不同于普通的可观察者,LiveData 遵守应用程序组件的生命周期,以便Observer 可以指定一个其应该遵守的Lifecycle。

注:在 Android 项目中导入 LiveData,请参阅添加组件到项目中

如果Observer 的Lifecycle 处于 STARTED 或 RESUMED 状态,LiveData 会认为Observer 处于活动状态。

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 监听的实现有 3 个重要部分:

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 -> {
                    // 更新 UI
                });
            }
        });
    }
}

请注意,addObserver() 方法将LifecycleOwner 作为第一个参数传递。这样做表示该观察者应该绑定到Lifecycle,意思是:

  • 如果Lifecycle 不处于活动状态(STARTED 或 RESUMED),即使该值发生变化也不会调用观察者。

  • 如果Lifecycle 被销毁,那么自动移除观察者。

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

可能会有多个 fragment 和 activity 在观察 MyLocationListener 实例,LiveData 可以规范的管理它们,以便只有当它们中的任何一个可见(即处于活动状态)时才连接到系统服务。

LiveData 有以下优点:

  • 没有内存泄漏:因为Observer 被绑定到它们自己的Lifecycle 对象上,所以,当它们的Lifecycle 被销毁时,它们能自动的被清理。

  • 不会因为 activity 停止而崩溃:如果Observer 的Lifecycle 处于闲置状态(例如:activity 在后台时),它们不会收到变更事件。

  • 始终保持数据最新:如果Lifecycle 重新启动(例如:activity 从后台返回到启动状态)将会收到最新的位置数据(除非还没有)。

  • 正确处理配置更改:如果 activity 或 fragment 由于配置更改(如:设备旋转)重新创建,将会立即收到最新的有效位置数据。

  • 资源共享:可以只保留一个 MyLocationListener 实例,只连接系统服务一次,并且能够正确的支持应用程序中的所有观察者。

  • 不再手动管理生命周期你可能已经注意到,fragment 只是在需要的时候观察数据,不用担心被停止或者在停止之后启动观察。由于 fragment 在观察数据时提供了其Lifecycle,所以LiveData 会自动管理这一切。

LiveData 的转换

有时候可能会需要在将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 的值并解包,然后将结果传递到下游。传递给switchMap() 的方法必须返回一个Lifecycle

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

使用这些转换允许在整个调用链中携带观察者的Lifecycle 信息,以便只有在观察者观察到LiveData 的返回时才运算这些转换。转换的这种惰性运算性质允许隐式的传递生命周期相关行为,而不必添加显式的调用或依赖。

每当你认为在ViewModel 中需要一个Lifecycle 类时,转换可能是解决方案。

例如:假设有一个 UI,用户输入一个地址然后会收到该地址的邮政编码。该 UI 简单的ViewModel 可能像这样:

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

    private LiveData<String> getPostalCode(String address) {
       // 不要这样做!!!
       return repository.getPostCode(address);
    }
}

如果是像这种实现,UI 需要先从之前的LiveData 注销并且在每次调用 getPostalCode() 时重新注册到新的实例。此外,如果 UI 被重新创建,它将会触发新的 repository.getPostCode() 调用,而不是使用之前的调用结果。

不能使用那种方式,而应该实现将地址输入转换为邮政编码信息。

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,因为它永远不会改变。postalCode 被定义为 addressInput 的转换,所以当 addressInput 改变时,如果有处于活动状态的观察者,repository.getPostCode() 将会被调用。如果在调用时没有处于活动状态的观察者,在添加观察者之前不会进行任何运算。

该机制允许以较少的资源根据需要惰性运算来创建LiveData。ViewModel 可以轻松获取到LiveData 并在它们上面定义转换规则。

创建新的转换

在应用程序中可能会用到十几种不同的特定转换,但是默认是不提供的。可以使用MediatorLiveData 实现自己的转换,MediatorLiveData 是为了用来正确的监听其它LiveData 实例并处理它们发出的事件而特别创建的。MediatorLiveData 需要特别注意正确的向源LiveData 传递其处于活动/闲置状态。有关详细信息,请参阅Transformations 类。


以上所述就是小编给大家介绍的《[译] Architecture Components 之 LiveData》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法霸权

算法霸权

[美] 凯西·奥尼尔 / 马青玲 / 中信出版集团 / 2018-9-1 / 69.00元

数据科学家凯西•奥尼尔认为,我们应该警惕不断渗透和深入我们生活的数学模型——它们的存在,很有可能威胁到我们的社会结构。 我们生活在一个依赖“算法”的时代,它对我们生活的影响越来越大,我们去哪里上学,我是不是应该贷款买车,我们应该花多少钱来买健康保险,这些都不是由人来决定的,而是由大数据模型来决定的。从理论上来说,这一模型应该让社会更加公平,每一个人的衡量标准都是一样的,偏见是不存在的。 ......一起来看看 《算法霸权》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具