1. LiveData介绍
1.1 作用
LiveData是Jetpack AAC的重要组件,同时也有一个同名抽象类。
LiveData,原意是 活着的数据。 数据还能有生命? 先来看下官方的定义:
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity/Fragment)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
拆解开来:
- LiveData是一个数据持有者,给源数据包装一层。
- 源数据使用LiveData包装后,可以被observer观察,数据有更新时observer可感知。
- observer的感知,只发生在(Activity/Fragment)活跃生命周期状态(STARTED、RESUMED),LiveData 只会将更新通知给活跃的观察者 。
1.2 特点
使用 LiveData 具有以下优势:
-
确保界面符合数据状态
当生命周期状态变化时,LiveData通知Observer对象,可以在observer对象中更新界面。观察者可以在生命周期状态更改时刷新界面,而不是在每次数据变化时刷新界面。
-
不会发生内存泄漏
observer会在LifecycleOwner状态变为DESTROYED后自动清理 remove。
-
不会因 Activity 停止而导致崩溃
如果LifecycleOwner生命周期处于非活跃状态(如返回栈中的Activity),则它不会接收任何 LiveData事件。
-
不需要手动解除观察
开发者不需要在onPause或onDestroy方法中解除对LiveData的观察,因为LiveData能感知生命周期状态变化,所以会自动管理所有这些操作。
-
数据始终保持最新状态
数据更新时 若LifecycleOwner为非活跃状态,那么会在变为活跃时接收最新数据。例如,曾经在后台的 Activity 会在返回前台后,observer立即接收最新的数据。
-
适当的配置更改
如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
共享资源
您可以使用单例模式扩展
LiveData
对象以封装系统服务,以便在应用中共享它们。LiveData
对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察LiveData
对象。如需了解详情,请参阅扩展 LiveData。
2. LiveData 的使用
2.1 基本使用
依赖:
下面来看基本用法:
- 创建LiveData实例,指定源数据类型。这通常在
ViewModel
类中完成。 - 创建Observer实例,实现onChanged()方法,用于接收源数据变化并刷新UI。可以控制当
LiveData
对象存储的数据更改时会发生什么。通常情况下,在 Activity 或 Fragment 中创建Observer
对象。 - LiveData实例使用observe()方法添加观察者,并传入LifecycleOwner,这样会使
Observer
对象订阅LiveData
对象,以使其收到有关更改的通知。通常情况下,在 Activity 或 Fragment 中附加Observer
对象。 - LiveData实例使用setValue() / postValue()更新源数据 (子线程要postValue())
实例:
创建 LiveData 对象
LiveData 是一种可用于任何数据的封装容器,其中包括可实现 Collections
的对象,如 List
。LiveData
对象通常存储在 ViewModel
对象中,并可通过 getter 方法进行访问,如以下示例中所示:
public class NameViewModel extends ViewModel { // 自定义ViewModel
// Create a LiveData with a String
private MutableLiveData<String> currentName; // MutableLiveData -> LiveData的实现类
public MutableLiveData<String> getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData<String>();
}
return currentName;
}
// Rest of the ViewModel...
}
复制代码
注意:请确保用于更新界面的 LiveData
对象存储在 ViewModel
对象中,而不是将其存储在 Activity 或 Fragment 中,原因如下:
① 避免 Activity 和 Fragment 过于庞大。现在,这些界面控制器负责显示数据,但不负责存储数据状态。
② 将 LiveData
实例与特定的 Activity 或 Fragment 实例分离开,并使 LiveData
对象在配置更改后继续存在。
观察 LiveData 对象
在大多数情况下,应用组件的 onCreate()
方法是开始观察 LiveData
对象的正确着手点,原因如下:
- 确保系统不会从 Activity 或 Fragment 的
onResume()
方法进行冗余调用。 - 确保 Activity 或 Fragment 变为活跃状态后具有可以立即显示的数据。一旦应用组件处于
STARTED
状态,就会从它正在观察的LiveData
对象接收最新值。只有在设置了要观察的LiveData
对象时,才会发生这种情况。
通常,LiveData 仅在数据发生更改时才发送更新,并且仅发送给活跃观察者。此行为的一种例外情况是,观察者从非活跃状态更改为活跃状态时也会收到更新。此外,如果观察者第二次从非活跃状态更改为活跃状态,则只有在自上次变为活跃状态以来值发生了更改时,它才会收到更新。
以下示例代码说明了如何开始观察 LiveData
对象:
public class NameActivity extends AppCompatActivity {
private NameViewModel model;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code to setup the activity...
// 获取ViewModel -> 通过ViewModelProvider进行initViewModel的操作
model = new ViewModelProvider(this).get(NameViewModel.class);
// 创建 observer对象更新UI
final Observer<String> nameObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable final String newName) {
// 更新UI
nameTextView.setText(newName);
}
};
// 调用 observe() 方法 真正实现 -> 观察ViewModel的数据
model.getCurrentName().observe(this, nameObserver);
}
}
复制代码
在传递 nameObserver
参数的情况下调用 [observe()
](developer.android.google.cn/reference/a…, android.arch.lifecycle.Observer)) 后,系统会立即调用 onChanged()
,从而提供 mCurrentName
中存储的最新值。如果 LiveData
对象尚未在 mCurrentName
中设置值,则不会调用 onChanged()
。
更新 LiveData 对象
LiveData 没有公开可用的方法来更新存储的数据。MutableLiveData
类将公开 setValue(T)
和 postValue(T)
方法,如果您需要修改存储在 LiveData
对象中的值,则必须使用这些方法。通常情况下会在 ViewModel
中使用 MutableLiveData
,然后 ViewModel
只会向观察者公开不可变的 LiveData
对象。
设置观察者关系后,您可以更新 LiveData
对象的值(如以下示例中所示),这样当用户点按某个按钮时会触发所有观察者:
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "John Doe";
model.getCurrentName().setValue(anotherName);
}
});
复制代码
在本示例中调用 setValue(T)
导致观察者使用值 John Doe
调用其 onChanged()
方法。本示例中演示的是按下按钮的方法,但也可以出于各种各样的原因调用 setValue()
或 postValue()
来更新 mName
,这些原因包括响应网络请求或数据库加载完成。在所有情况下,调用 setValue()
或 postValue()
都会触发观察者并更新界面。
注意:您必须调用 setValue(T)
方法以从主线程更新 LiveData
对象。如果在工作器线程中执行代码,您可以改用 postValue(T)
方法来更新 LiveData
对象。
将 LiveData 与 Room 一起使用
Room 持久性库支持返回 LiveData
对象的可观察查询。可观察查询属于数据库访问对象 (DAO) 的一部分。
当数据库更新时,Room 会生成更新 LiveData
对象所需的所有代码。在需要时,生成的代码会在后台线程上异步运行查询。此模式有助于使界面中显示的数据与存储在数据库中的数据保持同步。您可以在 Room 持久性库指南中详细了解 Room 和 DAO。
LiveData 感受Activity 活动生命周期的例子
public class LiveDataTestActivity extends AppCompatActivity{
private MutableLiveData<String> mLiveData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lifecycle_test);
//liveData基本使用
mLiveData = new MutableLiveData<>();
// 调用 observe() 方法进行数据观察
mLiveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "onChanged: "+s);
}
});
Log.i(TAG, "onCreate: ");
mLiveData.setValue("onCreate");//activity是非活跃状态,不会回调onChanged。变为活跃时,value被onStart中的value覆盖
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart: ");
mLiveData.setValue("onStart");//活跃状态,会回调onChanged。并且value会覆盖onCreate、onStop中设置的value
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume: ");
mLiveData.setValue("onResume");//活跃状态,回调onChanged
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause: ");
mLiveData.setValue("onPause");//活跃状态,回调onChanged
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop: ");
mLiveData.setValue("onStop");//非活跃状态,不会回调onChanged。后面变为活跃时,value被onStart中的value覆盖
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy: ");
mLiveData.setValue("onDestroy");//非活跃状态,且此时Observer已被移除,不会回调onChanged
}
}
复制代码
注意到 LiveData实例mLiveData的创建是使用MutableLiveData,它是LiveData的实现类,且指定了源数据的类型为String。然后创建了接口Observer的实例,实现其onChanged()方法,用于接收源数据的变化。observer和Activity一起作为参数调用mLiveData的observe()方法,表示observer开始观察mLiveData。然后Activity的所有生命周期方法中都调用了mLiveData的setValue()方法。 结果日志打印如下:
//打开页面
2020-11-22 20:23:29.865 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onCreate:
2020-11-22 20:23:29.867 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onStart:
2020-11-22 20:23:29.868 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onStart
2020-11-22 20:23:29.869 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onResume:
2020-11-22 20:23:29.869 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onResume
//按Home键
2020-11-22 20:23:34.349 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onPause:
2020-11-22 20:23:34.349 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onPause
2020-11-22 20:23:34.368 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onStop:
//再点开
2020-11-22 20:23:39.145 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onStart:
2020-11-22 20:23:39.146 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onStart
2020-11-22 20:23:39.147 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onResume:
2020-11-22 20:23:39.147 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onResume
//返回键退出
2020-11-22 20:23:56.753 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onPause:
2020-11-22 21:23:56.753 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onPause
2020-11-22 20:23:58.320 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onStop:
2020-11-22 20:23:58.322 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onDestroy:
复制代码
- 首先打开页面,onCreate()中setValue,由于activity是非活跃状态,不会立即回调onChanged。当走到onStart()变为活跃时,onChanged被调用,但value被onStart()中setValue的value覆盖,所以打印的是onChanged: onStart。(为啥不是连续打印两次呢?: 是因为ON_START事件是在onStart() return之后,即onStart()走完之后才变为活跃,此时observer接收最新的数据。) 接着走到onResume(),也setValue了,同样是活跃状态,所以立刻回调onChanged,打印onChanged: onResume
- 按Home键时,onPause()中setValue,活跃状态,立刻回调onChanged方法。onStop()执行时已经变为非活跃状态,此时setValue不会立即回调onChanged方法。
- 再点开时,走到onStart()变为活跃时,onChanged被调用,但value被onStart()中setValue的value覆盖,所以打印的是onChanged: onStart。接着走到onResume(),也setValue了,同样是活跃状态,所以立刻回调onChanged。
- 返回键退出时,onPause()/onStop()的效果和按Home键一样。onDestroy()中setValue,此时非活跃状态,且此时observer已被移除,不会回调onChanged。
另外,除了使用observe()方法添加观察者,也可以使用observeForever(Observer) 方法来注册未关联 LifecycleOwner的观察者。在这种情况下,观察者会被视为始终处于活跃状态。
2.2 拓展使用
扩展包括两点:
- 自定义LiveData,本身回调方法的覆写:onActive()、onInactive()。
- 实现LiveData为单例模式,便于在多个Activity、Fragment之间共享数据。
官方的例子如下:
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);//监听到股价变化 使用setValue(price) 告知所有活跃观察者
}
};
//获取单例
@MainThread
public static StockLiveData get(String symbol) {
if (sInstance == null) {
sInstance = new StockLiveData(symbol);
}
return sInstance;
}
private StockLiveData(String symbol) {
stockManager = new StockManager(symbol);
}
//活跃的观察者(LifecycleOwner)数量从 0 变为 1 时调用
@Override
protected void onActive() {
stockManager.requestPriceUpdates(listener);//开始观察股价更新
}
//活跃的观察者(LifecycleOwner)数量从 1 变为 0 时调用。这不代表没有观察者了,可能是全都不活跃了。可以使用hasObservers()检查是否有观察者。
@Override
protected void onInactive() {
stockManager.removeUpdates(listener);//移除股价更新的观察
}
}
复制代码
为了观察股票价格变动,继承LiveData自定义了StockLiveData,且为单例模式,只能通过get(String symbol)方法获取实例。 并且重写了onActive()、onInactive(),并加入了 开始观察股价更新、移除股价更新观察 的逻辑。
- onActive()调用时机为:活跃的观察者(LifecycleOwner)数量从 0 变为 1 时。
- onInactive()调用时机为:活跃的观察者(LifecycleOwner)数量从 1 变为 0 时。
也就是说,只有当 存在活跃的观察者(LifecycleOwner)时 才会连接到 股价更新服务 监听股价变化。使用如下:
public class MyFragment extends Fragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//获取StockLiveData单实例,添加观察者,更新UI
StockLiveData.get(symbol).observe(getViewLifecycleOwner(), price -> {
// Update the UI.
});
}
}
复制代码