LiveData是一个可观察的数据持有者类。与常规的可观察数据不同,LiveData具有生命周期意识,它响应其他应用程序组件的生命周期,如Activity、Fragment或Service。这种意识确保LiveData只更新处于活动生命周期状态的应用程序组件观察者。
LiveData的观察者在其生命周期处于STARTED或RESUMED状态时处于活动状态。LiveData只通知处于活动状态的观察者有关更新的信息,非活动状态的观察者不会收到有关更改的通知。
注册一个Observer的时候同时传入对应的实现了LifecycleOwner接口的对象观察者。这样在相应生命周期对象的状态更改为DESTROYED时删除观察者。这对Activity和Fragment特别有用,因为它们可以安全地观察LiveData对象,而不必担心泄漏,当它们的生命周期被destroy时,活动和片段会立即取消订阅。
1.1. Why LiveData?
Ensures your UI matches your data state
LiveData遵循观察者模式。LiveData在生命周期状态更改时通知观察者对象。您可以合并代码来更新这些观察者对象中的UI。与每次应用数据更改时更新用户界面不同,观察者可以在每次发生更改时更新用户界面。
No memory leaks
当相关的生命周期被destroy时,观察者被绑定到对象上并在自己之后进行清理。
No crashes due to stopped activities
如果观察者的生命周期处于非活动状态,例如在后台堆栈中的Activity中,那么它不会接收任何LiveData事件。
No more manual lifecycle handling
UI组件只需要观察相关数据的变化,不需要做停止或继续观察的操作。LiveData自动管理所有这一切,因为它在观察时知道相关的生命周期状态变化。
Always up to date data
如果生命周期变为非活动状态,它将在再次变为活动状态时接收最新数据。例如,位于后台的Activity在返回前台后立即接收最新数据。
Proper configuration changes
如果Activity或者Fragment被重新创建,比如configuration改变事件,设备旋转,将立即接收到最新的数据。
Sharing resources
您可以使用singleton模式扩展一个[
livedata
]对象来包装系统服务,以便在您的应用程序中共享它们。LiveData对象连接到系统服务一次,然后任何需要使用该资源的观察者都可以监听该LiveData对象,更多查看: Extend LiveData。
1.2. Work with LiveData
Follow these steps to work with LiveData
objects:
- 创建一个持有指定数据的
LiveData
实例,通常这些工作是在ViewModel中完成的。 - 创建一个
Observer
对象并实现onChanged()
) 方法, 当LiveData
持有的数据发生改变时该方法将被触发,通常在Fragment或者Activitiy中创建Observer
. - 使用
observe()
) 方法将Observer
对象附加到LiveData
对象。observe()方法还需要一个LifecycleOwner对象。这会将观察者对象的活动状态绑定到该对象上。通常将观察者对象附加到UI控制器中,例如Activity或Fragment。
当更新存储在LiveData对象中的值时,只要附加的LifecycleOwner处于活动状态,它就会触发所有注册的观察者。LiveData允许UI控制器观察器订阅更新。当LiveData对象持有的数据更改时,UI会自动更新以响应。
1.2.1. Dependencies
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'android.arch.lifecycle:viewmodel:1.1.1'
implementation 'android.arch.lifecycle:livedata:1.1.1'
implementation 'android.arch.lifecycle:extensions:1.1.1'
1.2.2. Create LiveData
LiveData是一个包装器,可以用于任何数据,包括实现集合的对象,如列表。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;
}
// Rest of the ViewModel...
}
初始化时,LiveData所持有的数据未被设置。
1.2.3. Observe LiveData
在大多数情况下,应用程序组件的onCreate()方法是开始观察LiveData对象的正确位置,原因如下:
- 以确保系统不会从Activity或Fragment的onResume()方法中进行冗余调用。
- 以确保Activity或Fragment具有可以在active时立即显示的数据。一旦应用程序组件处于active状态,它就会从所观察的LiveData对象接收最新的值。只有在设置了要观察的LiveData对象时才会发生这种情况。
- 通常,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);
}
}
1.2.4. Update LiveData
LiveData没有公开的方法来更新存储的数据。 MutableLiveData
类提供了setValue(T)) 和 postValue(T)
) 方法,如果需要修改存储在LiveData中的数据,则需要使用它。通常 MutableLiveData
使用在 ViewModel
对象中用于设置存储的值,将不可修改的 LiveData
对象提供给Observers。
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "John Doe";
model.getCurrentName().setValue(anotherName);
}
});
在示例中调用setValue(t)会导致观察者的onChanged()方法获取到name的新值John Doe
。示例显示了一个按钮按下,可以调用setValue()或postValue()以更新mname,包括响应网络请求或完成数据库加载;在所有情况下,对setValue()或postValue()的调用都会触发观察者并更新UI。
必须调用setValue(t)方法从主线程更新LiveData对象。如果代码在工作线程中执行,则可以使用postValue(t)方法来更新LiveData对象。
1.2.5. Use LiveData with Room
持久化库 Room 支持可观察的查询,这些查询会返回LiveData对象。可观察的查询作为数据库访问对象(DAO)的一部分写入。 Room会生成所有必要的代码,以便在数据库更新时更新LiveData对象。生成的代码在worker线程上执行异步查询,此模式对于保持用户界面中显示的数据与存储在数据库中的数据同步非常有用。阅读更多有关Room和DAOS的信息: Room persistent library guide.
1.2.6. Use coroutines
LiveData
包含对Kotlin协程的支持,具体查看: Use Kotlin coroutines with Android Architecture Components.
1.3. Extend LiveData
如果观察者的生命周期处于STARTED或RESUMED状态,则LiveData会将观察者视为处于活动状态。下面的示例代码说明如何扩展LiveData类:
public class StockLiveData extends LiveData<BigDecimal> {
private StockManager stockManager;
private SimplePriceListener listener = new SimplePriceListener() {
@Override
public void onPriceChanged(BigDecimal price) {
setValue(price);
}
};
public StockLiveData(String symbol) {
stockManager = new StockManager(symbol);
}
@Override
protected void onActive() {
stockManager.requestPriceUpdates(listener);
}
@Override
protected void onInactive() {
stockManager.removeUpdates(listener);
}
}
PriceListener的实现包括以下重要的方法:
- 当
LiveData
有一个active的observer时onActive()
) 方法被调用. - 当
LiveData
没有任何一个observer时,onInactive()
) 方法被调用. setValue(T)
) 方法更新LiveData
中的数据并通知处于active状态的observer.
您可以使用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()
) 方法传入了fragment对象作为第一个参数,它是一个LifecycleOwner
对象实例,这样使得observer绑定到和持有者fragment关联的生命周期对象。
- If the
Lifecycle
object is not in an active state, then the observer isn't called even if the value changes. - After the
Lifecycle
object is destroyed, the observer is automatically removed.
LiveData对象具有生命周期意识,这意味着您可以在多个Activity、Fragment和Service之间共享它们。为了保持示例的简单性,可以将LiveData类实现为单例,如下所示:
public class StockLiveData extends LiveData<BigDecimal> {
private static StockLiveData sInstance;
private StockManager stockManager;
private SimplePriceListener listener = 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;
}
// ......
}
您可以在Fragment中使用它,如下所示:
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
StockLiveData.get(symbol).observe(this, price -> {
// Update the UI.
});
}
}
多个Fragment和Acivity可以观察MyPriceListener实例。只有当其中一个或多个处于visible和active状态时,LiveData才会连接到系统服务。
1.3.1. Transform LiveData
在将LiveData对象发送给观察者之前,您可能需要更改该对象中存储的值,或者您可能需要返回另一个LiveData实例的值而不直接返回LiveData实例本身。 Lifecycle
包提供了转换类 Transformations
,其中包括支持这些场景的方法。
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
与map()类似,将函数应用于存储在LiveData对象中的值,并将结果解包并发送到下游。传递给switchmap()
的函数必须返回LiveData对象,如下面的示例所示:
private LiveData<User> getUser(String id) {
...;
}
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
您可以使用转换方法在观察者的生命周期中携带信息。transfor不会执行除非当前LiveData有观察者对象,由于转换是延迟计算的,因此与生命周期相关的行为是隐式传递的,不需要额外的显式调用或依赖项。
如果在你的 ViewModel
对象中需要一个 Lifecycle
对象,使用转换可能是一个更好的方案。假设您有一个接受地址并返回该地址邮政编码的UI组件。如下面的示例代码所示:
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字段被定义为Address输入的转换。只要你的应用程序有一个与postalCode字段相关联的活动观察者,每当address输入改变时,该字段的值就会重新计算和检索。
1.3.2. Create new transformations
有十几种不同的特定转换可能在您的应用程序中很有用,但默认情况下并没有提供它们。要实现您自己的转换,您可以使用mediatorlivedata
类,它监听其他livedata
对象并处理它们发出的事件。mediator livedata
将其状态正确传播到源livedata
对象。要了解有关此模式的更多信息,请参阅transformations
类的参考文档。
1.4. Merge multiple LiveData
MediatorLiveData
是 LiveData
的子类,它允许我们合并多个LiveData数据源.只要有一个LiveData数据源数据发生改变, MediatorLiveData
的观察者就会被触发。
比如你有一个 LiveData
对象用于更新UI,它可以从database中更新,也可以从network中更新,我们可以把它们添加到 MediatorLiveData
对象中:
- A
LiveData
object associated with the data stored in the database. - A
LiveData
object associated with the data accessed from the network.
之后我们的UI只需要观察 MediatorLiveData
对象来更新UI。 Addendum: exposing network status section of the Guide to App Architecture.
1.5. Additional resources
To learn more about the LiveData
class, consult the following resources.
1.6. Samples
- Sunflower, a demo app demonstrating best practices with Architecture Components
- Android Architecture Components Basic Sample
1.7. More
20:30:41.581 14619-14619/com.tw.jetpackpro E/MainLifecycle: onCreate
20:30:41.586 14619-14619/com.tw.jetpackpro E/MainLifecycle: onStart
20:30:41.586 14619-14619/com.tw.jetpackpro E/MainActivity: ***LiveData.onActive***
20:30:41.587 14619-14619/com.tw.jetpackpro E/MainLifecycle: onResume
20:31:31.584 14619-14619/com.tw.jetpackpro E/MainActivity: startActivity-begin
20:31:31.605 14619-14619/com.tw.jetpackpro E/MainActivity: startActivity-end
20:31:31.612 14619-14619/com.tw.jetpackpro E/MainLifecycle: onPause
20:31:31.716 14619-14619/com.tw.jetpackpro E/LocationActivity: onCreate
20:31:31.726 14619-14619/com.tw.jetpackpro E/LocationActivity: onStart
20:31:31.728 14619-14619/com.tw.jetpackpro E/LocationActivity: onResume
20:31:32.358 14619-14619/com.tw.jetpackpro E/MainActivity: ***LiveData.onInactive***
20:31:32.358 14619-14619/com.tw.jetpackpro E/MainLifecycle: onStop
20:33:54.676 14619-14619/com.tw.jetpackpro E/MainLifecycle: onStart
20:33:54.676 14619-14619/com.tw.jetpackpro E/MainActivity: ***LiveData.onActive***
20:33:54.677 14619-14619/com.tw.jetpackpro E/MainLifecycle: onResume
20:33:55.199 14619-14619/com.tw.jetpackpro E/LocationActivity: onStop
20:33:55.200 14619-14619/com.tw.jetpackpro E/LocationActivity: onDestory
LiveData的Active状态,维持在LifecycleOwner(即Activity/Fragment)的onStart
—onStop
生命周期状态区间内,所以某些依赖于当前Activity生命周期的操作,如果要求必须至少是onResume状态,则需要谨慎使用。
- Activity A 在onStart之后,LiveData变为Active。
- startActivity,Activity A 生命周期从
onResume
-->onPause
约30ms。 - 新的Activity B在onResume之后,Activity A中的LiveData变为InActive,Activity A生命周期也变为
onStop
。 - 退出Activity B,Activity A生命周期变为
onStart
,随即LiveData也变为Active。