Android Jetpack 之 LiveData – 2

这是我参与更文挑战的第21天,活动详情查看: 更文挑战

LiveData 概述

自定义LiveData

前面介绍了自带的MutableLiveData,同样也可以自己定义LiveData类。

自定义一个BatteryLiveData类继承自LiveData,通过广播去接受系统电池电量,通过setValue将数据设置给LiveData。

public class StockLiveData extends LiveData<BigDecimal> {
    private StockManager mStockManager;

    private SimplePriceListener mListener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    public StockLiveData(String symbol) {
        mStockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        mStockManager.requestPriceUpdates(mListener);
    }

    @Override
    protected void onInactive() {
        mStockManager.removeUpdates(mListener);
    }
}
复制代码

该例所展示的价格监听器的实现包含了下列方法:

  • LiveData 有了活动状态的观测者时,其 onActive 方法被调用。这代表着您需要在这个方法中开始对股价的监听。
  • LiveData 没有任何处于活动状态的观测者时,其 onInactive 方法被调用。由于没有观测者在监听了,您就没必要让 StockManager 继续保持连接状态。
  • setValue(T) 方法更新 LiveData 示例的值,并把更新通知给每个处于活动状态的观测者。

您可以这样使用 StockLiveData

public class MyFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        LiveData<BigDecimal> myPriceListener = ...;
        myPriceListener.observe(this, price -> {
            // 更新 UI
        });
    }
}
复制代码

observe() 方法将 fragment 作为 LifecycleOwner 作为第一个参数。这样做是为了表明该观测者受限于该 LifecycleOwner 的生命周期 Lifecycle。因此:

  • 如果 Lifecycle 对象并不处于活动状态,那么即使数据发生了变化,观测者也不会被通知。
  • Lifecycle 对象被销毁后,观测者也会被自动移除。

单例LiveData

LiveData 对象能感知生命周期这一点也意味着,您可以将其分享给多个 activity、fragment 和 service。为了让示例代码简单一些,您可以用单例模式这样实现 LiveData

public class StockLiveData extends LiveData<BigDecimal> {
    private static StockLiveData sInstance;
    private StockManager mStockManager;

    private SimplePriceListener mListener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    @MainThread
    public static StockLiveData get(String symbol) {
        if (sInstance == null) {
            sInstance = new StockLiveData(symbol);
        }
        return sInstance;
    }

    private StockLiveData(String symbol) {
        mStockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        mStockManager.requestPriceUpdates(mListener);
    }

    @Override
    protected void onInactive() {
        mStockManager.removeUpdates(mListener);
    }
}
复制代码

并在 fragment 中这样使用它:

public class MyFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        StockLiveData.get(getActivity()).observe(this, price -> {
            // 更新 UI 
        });
    }
}
复制代码

MyPriceListener 示例可供多个 fragment 和 activity 观测,而 LiveData 只会在其中至少一个或多个处于可见的活动状态时,才会连接到系统服务。

和 Room 一起使用 LiveData

todo

Room 数据持久化库支持可观测的查询(observable queries),并返回 LiveData 对象。可观测的查询是数据库访问对象(Database Access Object,DAO)的一部分。

当数据库更新时,Room 会自动产生所有必要的代码来更新 LiveData 对象。在必要的时候,这些产生的代码会异步地运行在一个后台线程上。这种模式有利于保持 UI 展示的数据与数据库保持一致。欲了解更多有关 Room 和 DAO 的内容,请参阅 Room 持久化库

Transformations

todo

有时您可能会想要在 LiveData 对象把数据更新分发给观测者之前对数据进行更改,或者想要根据另一个 LiveData 的值来返回一个不同的 LiveData 实例。LiveData 包中的 Transformations 类提供了若干辅助函数来帮助您处理这些情况。

  • [Transformations.map()](developer.android.google.cn/reference/a…, android.arch.core.util.Function))

    把一个函数变换应用到 LiveData 对象所存储的值,并将结果向下传递。

     LiveData<User> userLiveData = ...;
     LiveData<String> userName = Transformations.map(userLiveData, 	user -> {
         user.name + " " + user.lastName
     });
    复制代码
  • [Transformations.switchMap()](developer.android.google.cn/reference/a…, android.arch.core.util.Function>))

    map() 相似地,把一个函数变换应用到 LiveData 对象所存储的值,并将结果拆包后向下传递。该函数变换必须返回一个 LiveData 对象,如下例所示:

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

您可以使用上述变换函数来在观测者的整个生命周期中传递信息。如果观测者没有监视返回的 LiveData 对象,那么变换函数的计算就不会被执行。由于变换是惰性求值的,生命周期相关的行为也会被隐式地传递,无须任何显式调用或依赖。

如果您在 ViewModel 对象中需要一个 LifeCycle 对象,那么变换可能是个更好的解决方案。譬如,假设您有一个 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 组件每次调用 getPostalCode() 时都需要从之前的 LiveData 取消订阅,然后再注册新实例的订阅。更糟的是,如果 UI 组件被重建的话,它将触发一次新的对 repository.getPostCode() 的调用,而不是利用上一次调用的结果。

与之相反,您应当把邮政编码的查询实现为一个从地址输入到 LiveData 的变换,如下例所示:

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 字段永不变化,因而是 publicfinal 的;postalCode 字段被定义为 addressInput 的变换,也就意味着 repository.getPostCode() 方法会在 addressInput 改变时被调用。当然,这只是有观测者的情况,如果在 repository.getPostCode() 被调用时没有活动状态的观测者,那么在添加观测者之前,计算都不会被执行。

这种机制允许应用的低级部分创建惰性求值的 LiveData 对象。一个 ViewModel 对象能轻易获得对 LiveData 对象的引用,然后在此基础上定义对其的变换。

创建新的变换

todo

您的应用可能需要一大堆各不相同的变换,但它们并不是默认提供的。想要实现自己的变换,您可以使用 MediatorLiveData 类,该类监听其他 LiveData 对象并处理它们发出的事件。MediatorLiveData 正确地将其状态传递给来源的 LiveData 对象。欲了解更多有关该模式的内容,请参阅 Transformations 的文档。

MediatorLiveData

MediatorLiveDataLiveData 的一个子类,允许您合并多个 LiveData 数据源。当任何 LiveData 数据源对象发生改变时, MediatorLiveData 对象的观测者都会被通知。

例如,如果您在 UI 中有一个可以被本地数据库或网络更新的 LiveData 对象,那么您可以将下列数据源添加到 MediatorLiveData 对象:

  • 一个与数据库中的数据关联的 LiveData 对象
  • 一个与从网络获取的数据关联的 LiveData 对象

您的 activity 只需观测 MediatorLiveData 对象来接收两个数据源的更新。欲了解更详尽的例子,请参阅应用架构指南中的“附录:显示网络状态”。

todo

欲了解更多有关和 Snackbar 信息、导航事件等一同使用 LiveData 的内容,请参阅该博客

参考

官方文档:LiveData 概览

Jetpack源码解析—LiveData的使用及工作原理

Android livedata 源码解剖

Android源码解析-LiveData

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享