本内容主要介绍如何使用 Android 架构组件中的 LiveData。
Android LiveData 的官方介绍文档:https://developer.android.com/topic/libraries/architecture/livedata
LiveData 是一个可观察的数据持有者类。与常规的 observable 不同(如果您不了解 observable,可以参阅 这里),LiveData 具有生命周期意识,这意味着它遵循其他 App 组件(比如 Activity、Fragment 或 Service)的生命周期。这种意识确保 LiveData 只更新处于活动生命周期的 App 组件观察者(Observer)。
如果一个 Observer 的生命周期处于 STARTED
(对 Activity 来说,onStart() 到 onPause()) 或 RESUMED
状态,那么 LiveData 认为它处于活动状态。LiveData 只通知活动 Observer 更新。不会通知非活动状态的 Observer 更新。
您可以注册一个与实现 LifecycleOwner 接口的对象配对的 Observer。当对应的 Lifecycle 对象变为 RESTROYED
状态时,这种关系确保 Observer 被移除。这对于 Activity 和 Fragment 非常有用,因为它们可以安全的观察 LiveData,而不用担心内存泄露——当 Activity 和 Fragment 的生命周期为 DESTROYED
时,它们立即被取消订阅。
总结:当数据发生变化时,自动更新 UI。并且在不再被需要的时候,会自动被销毁。
如果想使用 LiveData,需要你的 APP 或者 module 的 build.gradle
中添加依赖。AndroidX 前的版本和 AndroidX 版本使用不同的声明依赖方法:
AndroidX 前的版本
dependencies {
def lifecycle_version = "1.1.1"
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
// alternatively - just LiveData
implementation "android.arch.lifecycle:livedata:$lifecycle_version"
annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"
}
AndroidX 版本
dependencies {
def lifecycle_version = "2.0.0"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
// alternatively - just LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
}
点击 这里 可以查看最新依赖版本号和依赖声明方法。
使用 LiveData 对象的步骤如下:
创建一个保存特定类型数据的 LiveData 实例。通常在 ViewModel 类中完成。
创建一个定义了 onChanged() 方法的 Observer 对象。当 LiveData 对象持有的数据发生变化时,将回调onChanged() 方法用于执行相应处理。通常,您应该在 UI controller(例如 Activity 或 Fragment)中创建 Observer 对象。
使用 observe() 方法将 Observer 对象注册到 LiveData 对象上。observe() 方法还需要一个 LifecycleOwner 对象参数。这将确保当 LiveData 发生改变时,通知 Observer 进行更新。通常,您应该在 UI controller(例如 Activity 和 Fragment)中注册 Observer。
注意:您可以使用 observeForever(Observer) 方法注册一个没有关联 LifecycleOwner 对象的 Observer。在这种情况下,Observer 总是处于活动状态。您可以通过调用 removeObserver(Observer) 方法来移除这些 Observer。
当您更新存储在 LiveData 中数据时,只要对应的 LifecycleOwner 处于活动状态,那么注册的 Observer 将被触发。
LiveData 允许 UI controller Observer 订阅更新。当 LiveData 对象保存的数据发生变化时,UI 会自动更新。
LiveData 是一个包装器,可用于任何数据,包含实现集合(例如 List)的对象。一个 LiveData 对象通常是放在 ViewModel 对象中,通过 getter 方法访问,如下面的示例所示:
public class NameViewModel extends ViewModel {
// Create a LiveData with a String
private MutableLiveData<String> currentName;
public MutableLiveData<String> getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData<String>();
}
return currentName;
}
}
最初,没有设置 LiveData 对象中的数据。
注意:请确保将更新 UI 的 LiveData 对象放在 ViewModel 对象中,而不是在 Activity 或 Fragment 中,原因如下:
避免臃肿的 Activity 和 Fragment。UI controller 负责显示数据,而不保存数据状态。
将 LiveData 实例与特定的 Activity 或 Fragment 解耦,以及在配置发生变化后 LiveData 仍然有效。
通常,在 App 组件的 onCreate() 方法中开始观察 LiveData 对象,原因如下:
确保系统在 Activity 或 Fragment 的 onResume() 方法中进行冗余调用。
确保 Activity 或 Fragment 在变为活动状态时,拥有可显示的数据。一旦 App 组件处于 STARTED
状态,就会从它所观察的 LiveData 对象中接收最新的值。
通常,只有在数据发生变化时,LiveData 才会通知活动的 Observer 更新。一个例外是,当 Observer 从非活动状态变为活动状态时,也会收到更新。此外,如果 Observer 第二次从非活动状态变为活动状态,则只有在该值自上次变为活动状态以来发生变化时才会接受到更新。
下面的代码演示了如何开始观察 LiveData 对象:
public class NameActivity extends AppCompatActivity {
private NameViewModel model;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get the ViewModel.
model = ViewModelProviders.of(this).get(NameViewModel.class);
// Create the observer which updates the UI.
final Observer<String> nameObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable final String newName) {
// Update the UI, in this case, a TextView.
nameTextView.setText(newName);
}
};
// Observe the LiveData,
// passing in this activity as the LifecycleOwner and the observer.
model.getCurrentName().observe(this, nameObserver);
}
}
将 nameObserver
作为参数调用 observe() 后,将立即回调 onChanged(),以提供存储在 mCurrentName
中的最新值。如果 LiveData 对象没有设置值 mCurrentName
,将不会调用 onChanged()。
LiveData 没有提供公用的方法更新存储的数据。MutableLiveData 提供公用方法 setValue(T)
和 postValue(T)
,只能使用这两个方法来编辑存储在 LiveData 对象中的值。通常,在 ViewModel 中使用 MutableLiveData,然后 ViewModel 向 Observer 提供不可变的 LiveData 对象。
在建立观察者关系后,您可以更新 LiveData 对象的值,如下面的例子所示,当用户点击按钮时将处罚所有的 Observer:
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "John Doe";
model.getCurrentName().setValue(anotherName);
}
});
在示例中,调用 setValue(T)
将导致 Observer 调用 onChanged() 方法。
注意:更新 LiveData 对象时,在主线程中使用
setValue(T)
,在工作线程中使用postValue(T)
。
Room persistence library 支持返回 LiveData 对象的可观察查询。
当数据库更新时,Room 产生所有更新 LiveData 对象所需的代码。生成的代码在需要时将在后台线程上执行异步查询。这种模式有利于 UI 上显示的数据与数据库中的数据保持一致。
如果 Observer 的生命周期处于 STARTED
或 RESUMED
状态,那么 LiveData 认为它处于活动状态。下面的实例代码演示了如何扩展 LiveData 类:
public class StockLiveData extends LiveData<BigDecimal> {
private StockManager stockManager;
private SimplePriceListener listener = new SimplePriceListener() {
@Override
public void onPriceChanged(BigDecimal price) {
// 更新 LiveData 实例的值,并通知活动的 Observer 进行更新。
setValue(price);
}
};
public StockLiveData(String symbol) {
stockManager = new StockManager(symbol);
}
// 当 LiveData 对象有一个活动的 Observer 时,被调用。
@Override
protected void onActive() {
stockManager.requestPriceUpdates(listener);
}
// 当 LiveData 对象没有任何活动的 Observer 时,被调用。
@Override
protected void onInactive() {
stockManager.removeUpdates(listener);
}
}
您可以按照下面的方法使用 StockLiveData
:
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
LiveData<BigDecimal> myPriceListener = ...;
myPriceListener.observe(this, price -> {
// Update the UI.
});
}
}
observe() 方法接收 MyFragment(其为一个 LifecycleOwner 实例)作为第一个参数。这样做意味着 Observer 绑定到 Lifecycle 对象,即:
LiveData 对象具有感知生命周期的能力,意味着您可以在多个 Activity、Fragment 和 Service 之间共享它们。为了简洁,您可以使用单例模式实现 LiveData 类,如下所示:
public class StockLiveData extends LiveData<BigDecimal> {
private static StockLiveData sInstance;
private StockManager stockManager;
...
@MainThread
public static StockLiveData get(String symbol) {
if (sInstance == null) {
sInstance = new StockLiveData(symbol);
}
return sInstance;
}
...
}
多个 Activity 和 Fragment 能够观察 StockLiveData 实例。只有在它们中至少一个处于可见和活动状态时,LiveData 才会连接到系统服务。
您可能希望先修改存储在 LiveData 中的数据后,然后再将其分配给 Observer;或者您可能需要基于另一个 LiveData 的值返回一个不同的 LiveData 实例。Transformations 类可以满足这些需求,其提供以下两个函数:
Transformations.map()
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
当 userLiveData 对象的数据发生变化时,将通知 userName 对象进行更新。
Transformations.switchMap()
private LiveData<User> getUser(String id) {
...;
}
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
和使用 map() 一样,当 userId 对象的数据发生变化时,将通知 user 对象进行更新。与 map() 不同的是,switchMap() 的第二个参数需要一个 LiveData 对象;所以当第二参数的 LiveData 对象的数据发生变化时,也会通知 user 对象进行更新。
在 Observer 的生命周期中,您可以使用转换方法来传递信息。只要当一个 Observer 正在观察这个返回的 LiveData 对象时,这个转换才会执行。因为这个转换是延迟计算的,所以与生命周期相关的行为是隐式传递的,不需要额外的显示调用或依赖项。
如果您认为在 ViewModel 对象中需要 Lifecycle 对象,则转换可能是更好的解决方案。例如,假设您有一个接受 address 并返回该 address 的 postal code 的 UI 组件。您可以为这个组件实现一个简单的 ViewModel,如下面的代码所示:
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() 时,UI 组件都需要先注销注册之前的 LiveData 对象,然后注册新的 LiveData 实例。另外,当 UI 组件被重新创建时,将调用 repository.getPostCode() 方法,而不是使用之前的调用结果。
相反,您可以将 postal code 查找实现为 address 输入的转换,如下面的代码所示:
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
被定义为 addressInput
的转换(Transformation)。只要您的 App 有一个与 postalCode
相关的 Observer,当 addressInput
发生变化时都将重新计算和检索 postalCode
的值。
这种机制允许低级别的 App 创建按需延迟计算的 LiveData。ViewModel 对象可以轻松获取 LiveData 对象的引用,然后在其上定义转换规则。
您可以使用 MediatorLiveData 类来实现自己的转换。您可以点击 这里 了解更新关系 Transformations 的信息。
MediatorLiveData 是 LiveData 的一个子类,该类允许您合并多个 LiveData 源。当任何原始 LiveData 源对象发生变化时,MediatorLiveData 对象的观察者都将被触发。其提供了以下两个方法:
使用 LiveData,具有以下优点:
确保 UI 跟数据状态一致
LiveData 遵循观察者模式。当 Liftcycle 状态发生变化时,LiveData 将通知 Observer 对象。当数据发生变化时,Observer 将更新 UI。
没有内存泄露
Observer 会与 Lifecycle 对象绑定,从而当这个 Lifecycle 对象被销毁后 Observer 将会自行清理。
不会因为停止 Activity 出现崩溃
如果 Observer 处于非活动状态(例如,在后台堆栈中的 Activity),将不会收到任何 LiveData 事件。
不需要手动处理生命周期
UI 组件只需要观察相关数据,而不需要手动停止或回复观察。LiveData 会自动管理这些,因为在观察期间,它能够感知相关的生命周期状态变化。
始终保持最新数据
如果一个 Lifecycle 从非活动状态变为活动状态时,将接收到最新的数据。例如,当一个 Activity 从后台切换到前台时,将接收到最新的数据。
适当的配置更改
当一个 Activity 或 Fragment 由于配置更改(例如,屏幕旋转)进行重建时,它将立即接收到最新的可用数据。
共享资源
您可以使用单例模式扩展一个 LiveData 对象并包装成系统服务,以便在 App 中共享。LiveData 对象连接到系统服务后,任何需要这些资源的 Observer 只需要观察这个 LiveData 对象。
[1] https://developer.android.com/topic/libraries/architecture/livedata
[2] https://www.jianshu.com/p/87aa6464412b
[3] https://www.jianshu.com/p/8c15cadb638f