官方文档翻译
LiveData是一个observable数据持有类。与常规observable不同,LiveData是生命周期感知的,这意味着它跟随其他应用程序组件(如activities, fragments, or services)的生命周期。这种感知能力确保LiveData只更新处于活跃生命周期状态的应用程序组件。
注意:在Android工程中导入LiveData组件,请看Adding Components to your Project
LiveData与一个Observer关联,如果观察者的生命周期处于STARTED或RESUMED状态,则表示观察者处于活动状态。LiveData只通知活跃的观察者做更新。注册到LiveData对象中的不活跃的观察者则得不到数据更新的通知。
您可以注册一个observer并与实现了LifecycleOwner接口的对象配对。这种关系允许当相应的Lifecycle对象的状态改变为DESTROYED时,观察者被移除。这对于activities and fragments尤其有用,因为它们可以安全地观察LiveData对象,而不用担心泄漏——当activities 和 fragments的生命周期被销毁时,它们会立即取消订阅。
有关如何使用LiveData的更多信息,请参见使用LiveData对象。
使用LiveData具有以下优点:
LiveData遵循观察者模式。当生命周期状态改变时,LiveData通知Observer对象。您可以合并代码来更新这些观察者对象中的UI。观察者可以在数据每次有更新时更新UI。
观察者绑定到生命周期对象,并在其关联的生命周期是destroyed时自行清理。
如果观察者的生命周期是不活动的,比如在堆栈下面的activity,那么它接收不到任何LiveData事件。
UI组件仅仅观察相关数据,不停止或恢复观察。LiveData自动管理所有这一切,因为在观察的时候它能感知到相关的生命周期状态变化。
如果生命周期变得不活动,则在再次激活时接收最新数据。例如,后台中的activity在返回到前台后立即接收最新数据。
如果由于配置改变(如设备旋转)而重新创activity或fragment,则它立即接收最新的可用数据。
您可以使用Sigelon模式集成LiveData对象来包装系统服务,以便它们可以在您的应用程序中共享。一旦LiveData对象连接到系统服务,然后需要用到该资源的任何observer都可以观察到LiveData对象。有关更多信息,请参见扩展LiveData。
按照以下步骤使用LiveData对象:
创建一个LiveData实例来保存某种类型的数据。这通常是在ViewModel类中完成的。
创建一个Observer对象,该对象定义onChanged()方法,该方法响应LiveData对象中数据更改时发生的变化。通常在UI控制器中创建一个Observer对象,例如activity或fragment。
使用observe()方法将观察者对象与LiveData对象关联到一块。observe()方法使用LifecycleOwner对象。这将Observer对象向LiveData对象订阅,以便通知其更改。通常在UI控制器中添加Observer对象,例如activity 或者 fragment。
注意:您可以使用observeForever(Observer)方法注册一个没有关联LifecycleOwner对象的观察者。在这种情况下,观察者被认为总是活跃的,因此总是被通知更新。您可以通过调用removeObserver(Observer)方法删除这些观察者。
当更新LiveData
对象中存储的值时,只要所依附LifecycleOwner
处于活动状态,就会触发所有已注册的观察者。
LiveData允许UI控制器观察者订阅更新。当LiveData
对象保存的数据发生变化时,UI会自动响应更新。
LiveData是一种可以与任何数据一起使用的包装器,包括实现Collections的对象,如List。LiveData对象通常存储在ViewModel对象中,并通过getter方法访问,如下面的示例所示:
public class NameViewModel extends ViewModel {
// Create a LiveData with a String
private MutableLiveData mCurrentName;
public MutableLiveData getCurrentName() {
if (mCurrentName == null) {
mCurrentName = new MutableLiveData();
}
return mCurrentName;
}
// Rest of the ViewModel...
}
最初,LiveData对象中的数据没有设置。
注意:请确保将更新UI的LiveData对象存储在ViewModel对象中,而不是activity 或者 fragment中,原因如下:
避免臃肿的activities和fragments。现在这些UI控制器负责显示数据,但不保存数据状态。
将LiveData实例与特定activity 或者 fragment实例解耦, 并允许LiveData对象在配置更改时存活。
您可以ViewModel向导中阅读更多关于ViewModel类的优点和用法。
在大多数情况下,应用程序组件的onCreate()方法是开始观察LiveData对象的正确位置,原因如下:
以确保系统不从activity或fragment的onResume()方法中进行多余调用。
以确保activity或fragment具有可在其活跃后立即显示的数据。一旦应用程序组件处于STARTED状态,它就会从正在观察的LiveData对象接收最新的值。只有在LiveData对象被设置为可观察状态时才会发生。
通常,LiveData只在数据更改时才提供更新,并且只对活动的observers提供更新。这种行为的一个例外是,观察者在从非活动状态转变为活动状态时也会收到更新。此外,如果观察者第二次从非活动状态改变为活动状态,它仅接收到一个更新,如果自上一次变为活动状态的值发生了改变。
下面的示例代码说明如何开始观察LiveData
对象:
public class NameActivity extends AppCompatActivity {
private NameViewModel mModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code to setup the activity...
// Get the ViewModel.
mModel = ViewModelProviders.of(this).get(NameViewModel.class);
// Create the observer which updates the UI.
final Observer nameObserver = new Observer() {
@Override
public void onChanged(@Nullable final String newName) {
// Update the UI, in this case, a TextView.
mNameTextView.setText(newName);
}
};
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
mModel.getCurrentName().observe(this, nameObserver);
}
}
使用作为参数传递的nameObserver调用observe()之后,立即调用onChanged()以提供存储在mCurrentName中的最新值。如果LiveData对象没有在mCurrentName中设置值,则不调用onChanged()。
LiveData没有公开可用的方法来更新存储的数据。MutableLiveData类公开了setValue(T)和postValue(T)方法,如果需要编辑LiveData对象中存储的值,则必须使用这些方法。通常在ViewModel中使用MutableLiveData,然后ViewModel只向observers公开不可变的LiveData对象。
在建立了观察者关系之后,然后可以更新LiveData对象的值,如下面的示例所示,当用户点击按钮时触发所有观察者:
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "John Doe";
mModel.getCurrentName().setValue(anotherName);
}
});
调用实例中的setValue(T)并传递John Doe作为参数会导致观察者调用它们的onChanged()方法。该示例显示了按下按钮,但是由于各种原因,可以调用setValue()
或postValue()
来更新mName,包括响应网络请求或数据库加载完成;在所有情况下,对setValue()或postValue()的调用都会触发观察者并更新UI。
注意:您必须调用setValue(T)
方法来从主线程更新LiveData对象。如果在工作线程中执行代码,则可以使用postValue(T)
方法来更新LiveData对象。
Room持久性库支持可观察的查询,这些查询返回LiveData对象。Observable查询是作为数据库访问对象(DAO)的一部分编写的。
当数据库被更新时,Room生成所有必要的代码来更新LiveData对象。生成的代码在需要时异步地在后台线程上运行查询。此模式对于保持UI中显示的数据与存储在数据库中的数据同步是有用的。您可以阅读更多关于Room和DAOs在Room持久库指南。
如果观察者的生命周期处于STARTED或RESUMED状态,则LiveData认为观察者处于活动状态。
public class StockLiveData extends LiveData {
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);
}
}
本例中价格listener的实现包括以下重要方法:
当LiveData对象具有活动的观察者时, 调用onActive()
方法。这意味着你需要从这个方法开始观察股票价格的更新。
当LiveData
对象没有任何活动的观察者时调用onInactive()
方法。由于没有观察者在监听,所以没有理由保持与StockManager
服务的连接。
setValue(T)
方法更新LiveData实例的值,并将变化通知给任何活动的观察者。
您可以使用StockLiveData类如下:
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
LiveData myPriceListener = ...;
myPriceListener.observe(this, price -> {
// Update the UI.
});
}
}
observe()方法将作为LifecycleOwner实例的fragment作为第一个参数传递。这样做意味着这个观察者被绑定到与所有者相关联的Lifecycle对象,意思是:
如果生命周期对象不处于活动状态,则即使值改变,也不会调用observer。
在生命周期对象被销毁后,观察者将被自动移除。
LiveData对象是生命周期感知的,这意味着您可以在多个activities, fragments, 和 services之间共享它们。为了保持示例简单,可以将LiveData类作为单例的实现如下:
public class StockLiveData extends LiveData {
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);
}
}
你可以在片段中使用它如下:
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
StockLiveData.get(getActivity()).observe(this, price -> {
// Update the UI.
});
}
}
多个fragments和activities可以观察MyPriceListener实例。LiveData只连接系统服务,如果其中一个或多个系统可见和激活。
在将LiveData对象分发给观察者之前,您可能需要对存储在LiveData对象中的值进行更改,或者您可能需要基于另一个LiveData对象的值返回不同的LiveData实例。Lifecycle包提供转换类,其中包含支持这些方案的帮助方法。
Transformations.map()
对存储在LiveData对象中的值应用函数,并将结果传播到下游。
LiveData userLiveData = ...;
LiveData userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
Transformations.switchMap()
类似于map(),将函数应用到LiveData对象中存储的值中,并解包并将结果分发到下游。传递到switchMap()的函数必须返回一个LiveData对象,如下面的示例所示:
private LiveData getUser(String id) {
...;
}
LiveData userId = ...;
LiveData user = Transformations.switchMap(userId, id -> getUser(id) );
可以使用transformation方法在观察者的生命周期中传递信息。除非观察者正在观看返回的LiveData对象,否则这些转换不会被计算。因为转换是惰性计算的,所以与生命周期相关的行为是隐式传递的,而不需要额外的显式调用或依赖。
如果您认为在ViewModel对象中需要一个Lifecycle对象,转换可能是更好的解决方案。例如,假设您有一个UI组件,它接受一个地址并返回该地址的邮政编码。可以通过以下示例代码说明该组件的初级的ViewModel模型:
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);
}
}
然后,UI组件需要从以前的LiveData
对象中注销注册,并在每次调用getPostalCode()
时注册到新实例。此外,如果UI组件被重新创建,它将触发对repository.getPostCode()
方法的另一个调用,而不是使用前一个调用的结果。
相反,您可以实现postalcode 查询作为地址输入的转换,如下面的示例所示:
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
,因为字段从不更改。postalCode
字段定义为addressInput
的转换,这意味着在addressInput更改时调用repository.getPostCode()
方法。如果存在活动观察者,则这是正确的,如果在调用repository.getPostCode()
时没有活动观察者,则在添加观察者之前不进行任何计算。
该机制允许应用程序的较低级别创建按需求计算的LiveData
对象。ViewModel
对象可以很容易地获得对LiveData对象的引用,然后在它们上面定义转换规则。
在你的应用程序中有十几种不同的特定转换,但默认情况下它们不被提供。为了实现您自己的转换,您可以使用MediatorLiveData
类,该类侦听其他LiveData对象并处理它们发出的事件。MediatorLiveData
正确地将其状态传播到Source LiveData对象。若要了解此模式的更多信息,请参见Transformations的参考文档。
MediatorLiveData是LiveData的子类,允许您合并多个LiveData源。然后,每当原始LiveData源对象改变时,就会触发MediatorLiveData对象的观察者。
例如,如果在UI中有一个可以从本地数据库或网络更新的LiveData对象,则可以向MediatorLiveData对象添加以下源:
与数据库中存储的数据相关联的LiveData对象。
与从网络访问的数据相关联的LiveData对象。
您的activity只需要观察MediatorLiveData对象以从两个源接收更新。有关详细示例,请参阅附录:App体系结构指南中公开网络状态部分。
LiveData是一个Android Jetpack架构组件。在Sunflower demo应用程序中使用它。
有关使用LiveData
与Snackbar消息、navigation事件和其他事件有关的信息,请阅读此帖子。