JetPack之LiveData与ViewModel
1.简述
LiveData从字面上看是与生命周期相关的数据,是一个数据的持有者。谷歌官方说明是“LiveData 具有生命周期感知能力,如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。”
2.使用
LiveData是一个抽象类,内部没有暴露任何公共方法,它的最简单的实现者是MutableLiveData。使用时,通过setValue或postValue来发送数据内容,后者可以在子线程中调用。简单使用示例:
public class LiveDataActivity extends AppCompatActivity {
private static final String TAG = "LiveDataActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
MutableLiveData<String> liveData = new MutableLiveData<>();
liveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.e(TAG, "new data: " + s);
}
});
liveData.setValue("hello world");
}
}
输出为:
E/LiveDataActivity: new data: hello world
上面是LiveData的最简单的使用。事实上,Jetpack并不建议这样使用LiveData,请确保将用于更新界面的 LiveData 对象存储在 ViewModel 对象中,而不是将其存储在 Activity 或 Fragment 中,原因如下:
1)避免 Activity 和 Fragment 过于庞大。现在,这些界面控制器负责显示数据,但不负责存储数据状态。
2)将 LiveData 实例与特定的 Activity 或 Fragment 实例分离开,并使对象在配置更改后继续存在。
也就是说,LiveData一般在ViewModel中创建,以减少Activity中逻辑,同时避免Activity因为横竖屏切换导致的数据丢失。
我们将上述示例代码修改一下,使用LiveData来完成。先在项目的gradle中添加依赖(因为不同的版本中,ViewModel获取方式有点不同):
def lifecycle_version = "2.2.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
创建SimpleViewModel.java:
public class SimpleViewModel extends ViewModel {
private final MutableLiveData<String> mLiveData;
private static int mCurTime = 0;
public SimpleViewModel() {
mLiveData = new MutableLiveData<>();
mCurTime ++;
mLiveData.postValue("number " + mCurTime + ", viewModel hashcode: " + hashCode());//1
}
public LiveData<String> getDataNotify(){
return mLiveData;
}
}
最后Activity中代码变成如下:
public class LiveDataActivity extends AppCompatActivity {
private static final String TAG = "LiveDataActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
SimpleViewModel simpleViewModel = new ViewModelProvider(this)
.get(SimpleViewModel.class);
simpleViewModel.getDataNotify().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.e(TAG, "new data: " + s);
}
});
}
}
ViewModel不能使用new来创建,而应该通过ViewModelProvider来创建,原因是如果我们通过new的方式创建,和我们不使用ViewModel+Lifecycle+LiveData的方式没什么区别,而通过ViewModelProvider,内部会协助管理ViewModel的生命周期。
我们尝试横竖屏切换,按照代码逻辑,如果LiveData重建了,那么内部的mCurTime静态成员应该每次都会自增1。实际日志输出如下:
2020-06-14 18:00:46.448 4506-4506/com.hudson.newfeaturetest E/LiveDataActivity: new data: number 1, viewModel hashcode: 202833815
2020-06-14 18:00:53.435 4506-4506/com.hudson.newfeaturetest E/LiveDataActivity: new data: number 1, viewModel hashcode: 202833815
可以发现两次结果一模一样,而且即便LiveData在ViewModel构建时的注释1处已经postValue发送了数据,横竖屏切换之后,Activity中仍然能够获取到之前的内容,也就是说ViewModel似乎帮我们把数据存储起来了。这就跟ViewModel的生命周期相关了。
3 ViewModel的生命周期
ViewModel 对象存在的时间范围是从获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle开始,ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。如下图说明了 Activity 经历屏幕旋转而后结束的过程中所处的各种生命周期状态,Fragment也是类似的。
通常在系统首次调用 Activity 对象的 onCreate() 方法时请求 ViewModel。系统可能会在 Activity 的整个生命周期内多次调用 onCreate(),如在旋转设备屏幕时、内存不足回收Activity后的重建时。ViewModel 存在的时间范围是从您首次请求 ViewModel 直到 Activity 完成并销毁。
由于这个特性,ViewModel可以用于同一个Activity内部的多个Fragment之间来进行数据共享。
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), { item ->
// Update the UI.
});
}
}
这两个 Fragment 可以使用其 Activity 范围共享 ViewModel 来处理此类通信。
需要注意的是,ViewModel存储数据的特性并不是指持久性数据,而是指在它所处的LifecycleOwner的生命周期范围内的全局存储,只要我们能拿到这个LifecycleOwner,我们便可以拿到之前它创建的那个ViewModel。如果一个Activity通过返回键点击被销毁了,destroyed,那么ViewModel也将会执行onCleared方法来清理数据,这时候你重新打开该Activity,这时会新建一个ViewModel,而不是使用之前的ViewModel,因为旧Activity已经和原来的ViewModel解绑了。我们可以继续使用上面的示例,先按下back返回键回到主页,再重新进入,对比两次的日志:
2020-06-14 18:40:05.918 5386-5386/com.hudson.newfeaturetest E/LiveDataActivity: new data: number 1, viewModel hashcode: 118099635
2020-06-14 18:40:36.477 5386-5386/com.hudson.newfeaturetest E/LiveDataActivity: new data: number 2, viewModel hashcode: 20262912
可以看到内部的静态成员自增了。
4.ViewModel源码跟踪
4.1 ViewModel的创建过程
从前面代码中的ViewModelProvider开始,我们可以找到ViewModel的创建过程
ViewModelProvider.java
...
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
...
内部通过this()方式调用了自身的构造方法,并传入两个参数,一个是ViewModelStore,另一个是Factory。
ViewModelStore从字面意思上看,它应该是我们ViewModel的存储器,因为Activity本身可以有多个ViewModel,因此可以猜测内部维护了一个ViewModel列表。在我们调用的构造方法中,ViewModelStore是通过ViewModelStoreOwner来获取的,而我们传入的ViewModelStoreOwner实际上是Activity。果然,在我们的ComponentActivity中,实现了ViewModelStoreOwner接口,如下所示:
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner {
...
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
//noinspection ConstantConditions
if (lifecycle == null) {
throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
+ "constructor. Please make sure you are lazily constructing your Lifecycle "
+ "in the first call to getLifecycle() rather than relying on field "
+ "initialization.");
}
// ...
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();//1
}
}
}
});
if (19 <= SDK_INT && SDK_INT <= 23) {
getLifecycle().addObserver(new ImmLeaksCleaner(this));
}
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {//2
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();//3
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
...
}
内部在注释2处的方法中判断ViewModelStore是否为空,为空则创建新对象。注释3处的NonConfigurationInstances是Activity能在横竖屏切换时保持生命周期而不销毁的,从字面上看意思是与配置无关的实例。我们看下这个getLastNonConfigurationInstance方法:
/**
* Retrieve the non-configuration instance data that was previously
* returned by {@link #onRetainNonConfigurationInstance()}. This will
* be available from the initial {@link #onCreate} and
* {@link #onStart} calls to the new instance, allowing you to extract
* any useful dynamic state from the previous instance.
*
* <p>Note that the data you retrieve here should <em>only</em> be used
* as an optimization for handling configuration changes. You should always
* be able to handle getting a null pointer back, and an activity must
* still be able to restore itself to its previous state (through the
* normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this
* function returns null.
*
* <p><strong>Note:</strong> For most cases you should use the {@link Fragment} API
* {@link Fragment#setRetainInstance(boolean)} instead; this is also
* available on older platforms through the Android support libraries.
*
* @return the object previously returned by {@link #onRetainNonConfigurationInstance()}
*/
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
与之对应的还有一个onRetainNonConfigurationInstance()方法,这两个方法与onSaveInstanceState()和onRestoreInstanceState()方法类似,这两个方法可以直接使用Object来保存Activity销毁与重建的信息。从上面的getLastNonConfigurationInstance()注释中我们看到,大部分情况下,我们可以使用Fragment的setRetainInstance来设置Fragment是否跟随Activity重建而重建。
另外在注释1处通过getLifecycle().addObserver()来给Activity的生命周期添加了一个监听器,并在内部判断Lifecycle的事件为ON_DESTROY事件且不是由于配置信息变更引起时,调用了ViewModelStore的clear方法,我们跟踪到内部,如下
ViewModelStore.java
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
可以看到上面ViewModelStore中确实维护了一个String为key,ViewModel为value的HashMap,并在clear方法中遍历map,依次调用了ViewModel的clear()方法
ViewModel.java
...
@MainThread
final void clear() {
mCleared = true;
// Since clear() is final, this method is still called on mock objects
// and in those cases, mBagOfTags is null. It'll always be empty though
// because setTagIfAbsent and getTag are not final so we can skip
// clearing it
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);//2
}
}
}
onCleared();//1
}
...
在注释1处调用了我们之前ViewModel生命周期中的onCleared方法。因此验证了之前生命周期中所说的,如果Activity进入的Finished态的话,ViewModel生命周期也结束了。其实clear()方法中我们发现,ViewModel生命周期的结束,仅仅是把mCleared的标识位设置成了true,同时注释2处会调用Closeable对象的close方法,然后再调用了1处的onCleared()抽象方法。因此如果我们在ViewModel中执行一些异步耗时操作,内部逻辑依然会继续执行,直到全部完成,因此在ViewModel中,我们也应该注意这类问题,只不过这部分的内存泄漏并不会影响到Activity,因为ViewModelStore已经把它从Map中移除了。
回到之前的ViewModelProvider构造方法中,第二个参数是一个Factory,它的定义如下
ViewModelProvider.java
...
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
* <p>
*
* @param modelClass a {@code Class} whose instance is requested
* @param <T> The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();//1
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);//2
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);//3
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);//4
} else {
viewModel = (mFactory).create(modelClass);//5
}
mViewModelStore.put(key, viewModel);//6
return (T) viewModel;
}
...
可以看出,这是一个ViewModel的构建工厂,也就是说,我们外界可以提供一个用于构建我们自定义ViewModel的构建工厂,然后ViewModelProvider会通过它来构建我们的实例。由于我们没有传入构建工厂,因此ViewModelProvider会为我们创建默认的构建工厂(在部分版本中必须传入构建工厂)。
ViewModelProvider的构建完成了,我们接着看get方法,注释1处获取到了class的名字,并在注释2处和DEFAULT_KEY拼接起来,DEFAULT_KEY是"androidx.lifecycle.ViewModelProvider.DefaultKey",因此最终拼接起来是"androidx.lifecycle.ViewModelProvider.DefaultKey" + 名字。最终先在注释3处获取HashMap中是否已经存储了该值,有就直接获取,没有则通过注释4或5处利用之前的构建工厂创建,然后在6处缓存起来,之后返回。至此,ViewModel分析完毕。
4.2 LiveData的数据观察者注册过程
前面分析中,ViewModelStore内部维护了一个String-ViewModel的HashMap集合,并会在Activity销毁时进行一些清理工作,也知道了为什么ViewModel能够在Activity重建了之后也能存活。上面代码中ViewModel完成创建后,我们在Activity中拿到它,并通过它获取到LiveData,然后调用了observe方法。
LiveData.java
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {//1
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);//2
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);//3
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);//4
}
上述逻辑中,1处首先判断LifecycleOwner是否处于销毁状态,如果是,直接忽略;2处创建一个LifecycleBoundObserver包裹了LifecycleOwner和数据监听的Observer,并在3处把它通过调用putIfAbsent存储到一个Map集合中。这个地方与之前的LifecycleRegistry类似,在前面的Lifecycle分析中,LifecycleRegistry通过维护一个Map集合,保存了所有LifecycleOwner的生命周期监听者。这里类似,LiveData内部也维护了一个集合,保存对它这个数据变动的监听者列表,存储的类型是ObserverWrapper。
LifecycleBoundObserver.java
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
可以看到,LifecycleBoundObserver本身实现了LifecycleEventObserver接口,而LifecycleEventObserver接口又继承自LifecycleObserver,因此它本身也是一个LifecycleOwner(或者说Activity等)生命周期的观察者。而且在上述代码4处把它加入到了LifecycleOwner的观察者集合中。
咦?本来LiveData通过observe方法添加了一个自身数据变动的观察者到列表中,而添加到列表中的这个对象实例本身又是LifecycleOwner(或者说Activity等)的观察者。其实看上面LifecycleBoundObserver的构造方法中,外界传入的数据变动观察者交给了父类,也就是ObserverWrapper来完成。
看后面的代码逻辑,shouldBeActive()、removeObserver()是不是有点眼熟,没错在Lifecycle那篇文章中,Lifecycle的使用需要我们LifecycleOwner的监听者去主动查询LifecycleOwner的生命周期状态,然后决定是否继续进行数据的加载(那篇文章中是定位信息的获取),如果LifecycleOwner本身处于销毁状态,那么我们应该停止数据的加载操作。在这里,LiveData把外界传入的数据变动观察者移除了,也就是说LiveData将不再通知这个Observer数据变动了。这也就印证了最开始的LiveData的定义。
到这里,我们发现,原来LifecycleObserver中一些模板式的代码逻辑,现在都被LiveData帮助完成了,而我们只需要关注具体的数据获取就可以了。
4.3 LiveData的发布数据过程
LiveData通过setValue和postValue来给观察者发布数据,因此这是我们分析的入口。其实postValue本质还是调用了setValue来操作,因此只分析setValue方法。
LiveData.java
...
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");//1
mVersion++;
mData = value;
dispatchingValue(null);//2
}
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());//3
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {//4
observer.activeStateChanged(false);//5
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);//6
}
...
上面逻辑中,1处首先判断调用的线程是不是主线程,因为setValue方法是主线程调用的方法;2处开始分发;由于dispatchingValue传入的是null,因此调用到3处,可以看到是遍历了内部的ObserverWrapper元素,并调用considerNotify()方法;4处通过ObserverWrapper的shouldBeActive方法判断LifecycleOwner的生命状态,实际上是调用了
mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
来获取状态,如果不是活跃状态,调用5处以便LiveData来响应不同状态下的处理,并直接返回,这里可以看出,如果LifecycleOwner没有处于活动状态的话,后续逻辑不再执行,直接返回,进一步验证了开头LiveData的生命周期感知能力;6处调用到实际的Observer的onChanged方法,即我们Activity中创建的Observer中的逻辑中。
5.总结
5.1 整体逻辑都分析完成了,总结一下大概类之间的关系
5.2 来检验一下自己
Q:LiveData是什么?
A:LiveData是一个具有生命周期感知能力的数据持有者,对于数据变动的观察者,会有选择性地针对处在活跃状态的观察者发布数据。
Q:LiveData本身可以直接在Activity使用,为什么需要ViewModel这中间的一层?
A:1.能够避免Activity或Fragment中过于庞大,使得它们只负责展示数据,而存储数据的任务交给了LiveData,而LiveData存储在ViewModel中; 2.能够把LiveData与Activity或Fragment实例分离,并保证它们配置更改不会影响数据生命周期
Q:ViewModel是如何保证配置更改的生命周期的?
A:利用了Activity中的onRetainNonConfigurationInstance来存储数据,getLastNonConfigurationInstance来获取数据。在Activity中实现了ViewModelStoreOwner接口,内部创建了一个ViewModelStore,并根据前面两个方法在配置更改时保存和获取。ViewModelStoreOwner中维护了一个ViewModel的HashMap集合,创建ViewModel时首先通过ViewModelProvider会去判断ViewModelStore中是否已经存在该ViewModel,存在则直接获取,否则通过构建工厂创建。在Activity内部添加了LifecycleOwner的生命周期观察者,并在Activity等生命周期进入Finished状态时,会遍历ViewModelStoreOwner的clear方法来清理ViewModel。
Q:LiveData如何保证对不活动的观察者不发送数据?
A:首先在对LiveData添加观察者时,会把这个观察者包裹在一个LifecycleBoundObserver中,该类实现了LifecycleEventObserver接口,而LifecycleEventObserver实现了LifecycleObserver接口。之后把包裹对象向LifecycleOwner注册,这样便监听了Activity等的生命周期,并在监听方法中判断ON_DESTROY事件,在内部移除了对生命周期的监听;此外,在LiveData发布数据后,会遍历LifecycleBoundObserver(代码中时ObserverWrapper,实际对象类型是LifecycleBoundObserver),并通过内部持有的LifecycleOwner获取当前生命周期状态,如果不属于活跃状态,将不会继续执行发送数据,当它重新处于活跃状态时,下次发布数据变动,还会通知到它。