ViewModel 是 Android Jetpack 架构组件中的核心部分,旨在以生命周期感知的方式存储和管理与 UI 相关的数据。它通过保持数据的持久性,避免了配置更改(如屏幕旋转)导致的数据丢失。下面详细解释 ViewModel 的工作原理、使用到的设计模式及其实际使用方法。
一、ViewModel 工作原理
1. 生命周期感知和状态管理
ViewModel 是生命周期感知的,它的生命周期独立于 Activity 和 Fragment,因此在配置更改(如屏幕旋转)时,ViewModel 实例不会被销毁。这意味着在这些配置更改期间,数据可以保持不变,从而避免了不必要的数据重新加载和计算。
ViewModel的初始化和获取
ViewModelProvider
ViewModelProvider 是用来创建和获取 ViewModel 实例的工具类。它通过 ViewModelStore 来存储 ViewModel 实例,使其与 Activity 或 Fragment 的生命周期绑定。
java
复制代码
public class ViewModelProvider {
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
@NonNull
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get("androidx.lifecycle.ViewModelProvider.DefaultKey:" + canonicalName, modelClass);
}
@NonNull
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
// If we already have a viewmodel with the given key, it should be removed.
if (viewModel != null) {
// 清除现有的ViewModel实例
mViewModelStore.put(key, null);
}
}
// 创建新的ViewModel实例
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
}
ViewModelProvider 通过 get() 方法获取或创建 ViewModel 实例。它会首先检查 ViewModelStore 中是否已经存在具有指定 key 的 ViewModel 实例,如果存在且类型匹配,则直接返回;否则,会通过 Factory 创建新的实例并存储在 ViewModelStore 中。
ViewModelStore
ViewModelStore 是一个用于存储 ViewModel 实例的类。它是一个简单的 HashMap,将 key 映射到 ViewModel 实例。
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.onCleared();
}
mMap.clear();
}
}
在 ViewModelStore 中,ViewModel 实例的生命周期是与其对应的 ViewModelStoreOwner 绑定的。当 ViewModelStore 被清除时(如 Activity 或 Fragment 被销毁时),存储的 ViewModel 实例也会被清除,并调用它们的 onCleared() 方法。
ViewModel的生命周期管理
ViewModel 是生命周期感知的,这意味着它会在 Activity 或 Fragment 被销毁时释放资源。ViewModel 的核心生命周期管理逻辑体现在 ViewModelStore 的 clear() 方法中,当宿主(Activity 或 Fragment)的生命周期结束时,ViewModelStore 会调用 clear() 方法来清除所有的 ViewModel 实例。
java
复制代码
public abstract class ViewModel {
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
当 ViewModel 被移除或 ViewModelStore 被清除时,onCleared() 方法会被调用。开发者可以重写此方法来释放相关资源,如取消订阅、关闭数据库连接等。
ViewModelProvider.Factory
Factory 接口是 ViewModelProvider 用来创建 ViewModel 实例的工厂。Android 提供了一个默认实现 NewInstanceFactory,它通过反射来实例化 ViewModel。
java
复制代码
public static class NewInstanceFactory implements Factory {
@NonNull
@Override
@SuppressWarnings("ClassNewInstance")
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
try {
return modelClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
NewInstanceFactory 使用默认的无参构造函数来创建 ViewModel 实例,但如果 ViewModel 需要参数传递,则需要自定义 Factory。
通过以上的源码分析,我们可以看出 ViewModel 的实例对象管理机制主要依赖于以下几个关键点:
ViewModelProvider: 负责获取和创建ViewModel实例,并与ViewModelStore进行交互。ViewModelStore: 负责存储ViewModel实例,并管理其生命周期。ViewModelProvider.Factory: 负责创建ViewModel实例,通过Factory模式为ViewModelProvider提供扩展性。
屏幕旋转导致的 Activity 重建
当屏幕旋转时,Activity 会经历销毁然后重建的过程。通常情况下,这样的配置变化(如屏幕旋转)会导致 Activity 被销毁,并且会创建一个新的 Activity 实例。然而,ViewModel 在这一过程中能够保持实例对象并不会被重新创建。下面是 ViewModel 如何在 Activity 重建过程中保存实例对象的详细解释。
当屏幕旋转时,系统会销毁当前的 Activity 并创建一个新的 Activity 实例。具体流程如下:
onConfigurationChanged()->onPause()->onSaveInstanceState()->onStop()->onDestory() ->onCreate()->onStart()->onRestoreInstanceState()->onResume()
2. ViewModel 的持久化
ViewModel 的设计目标是帮助管理UI相关数据,使其能够在配置变化(如屏幕旋转)中保持不变。ViewModel 能够在 Activity 重建过程中保持实例对象,主要依赖于以下机制:
2.1 ViewModelStore
ViewModel 是存储在 ViewModelStore 中的,而 ViewModelStore 是与 Activity 或 Fragment 的 ViewModelStoreOwner 相关联的。关键在于,ViewModelStore 是在 Activity 的 onRetainNonConfigurationInstance() 方法中保持的,并且与新的 Activity 实例共享。
当 Activity 被重新创建时,ViewModelStore 会被保留,并与新的 Activity 实例关联。如下所示:
java
复制代码
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback {
@Nullable
@Override
public Object onRetainNonConfigurationInstance() {
return mViewModelStore;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
} else {
mViewModelStore = new ViewModelStore();
}
}
}
在这个过程中,系统会将 ViewModelStore 作为非配置实例保留,新的 Activity 实例在重建时会通过 getLastNonConfigurationInstance() 方法获取之前的 ViewModelStore 实例,并继续使用其中的 ViewModel 对象。
进一步解释,nonConfigurationInstance存放在什么地方呢?在 Android 中,nonConfigurationInstance存放在ActivityClientRecord对象中,最终与ActivityThread和Activity的生命周期管理相关联。以下是简要说明:
ActivityThread中的管理:ActivityThread是 Android 应用程序的主线程,负责管理应用程序的所有活动(Activity)等组件的生命周期。在ActivityThread中,当处理 Activity 的创建、恢复等操作时,会涉及到nonConfigurationInstance的处理。例如,在ActivityThread的performCreate等方法中,会根据是否存在已保存的nonConfigurationInstance来恢复 Activity 的相关状态。ActivityClientRecord的作用:ActivityClientRecord是ActivityThread内部用于记录 Activity 相关信息的一个类。它存储了与一个 Activity 实例相关的各种数据和状态,包括nonConfigurationInstance。当 Activity 需要保存其非配置相关的状态时,相关数据会被封装到nonConfigurationInstance中,并存储在ActivityClientRecord里。比如,在 Activity 由于配置变化(如屏幕旋转)而重新创建时,ActivityClientRecord中的nonConfigurationInstance就可以用来传递和恢复之前 Activity 保存的 ViewModel 等重要数据和状态信息。- 与
Activity的关联:Activity通过ActivityThread和ActivityClientRecord间接与nonConfigurationInstance关联。在Activity的onRetainNonConfigurationInstance方法中,可以将需要保存的对象(如包含ViewModelStore的nonConfigurationInstance)返回,以便在 Activity 重建时恢复。而在Activity的onCreate等方法中,可以通过getLastNonConfigurationInstance等方法获取nonConfigurationInstance,从而恢复之前保存的状态。
另外,在 Android 的更新中,可以通过 Kotlin 的委托属性来更简洁地初始化 ViewModel,不再需要手动使用 ViewModelProvider。
by viewModels():用于在Fragment或Activity中创建或获取与该组件生命周期绑定的ViewModel实例。by activityViewModels():用于在Fragment中获取与宿主Activity共享的ViewModel实例。
这些委托方法内部实际上还是使用了 ViewModelProvider,但对开发者隐藏了复杂性,使代码更加简洁和易读。
二、 LiveData 的核心原理
LiveData 是一个可观察的数据持有类,其核心在于管理观察者和响应数据变化。它的实现包括了数据存储、观察者管理和通知机制。
1.1 LiveData 的结构与源码
LiveData 的基本结构在前面已经展示,但可以进一步细化,特别是观察者的管理与数据分发部分。
java
复制代码
public abstract class LiveData<T> {
// 持有当前数据
private volatile T mData;
// 保存最后一次版本号,用于避免重复通知
private int mVersion = START_VERSION;
// 活跃观察者的数量
private int mActiveCount = 0;
// 观察者的容器,使用 SafeIterableMap 确保线程安全
private final SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>();
// 添加观察者
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
// 包装观察者
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
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);
}
// 设置新值,并通知活跃的观察者
protected void setValue(T value) {
mData = value;
mVersion++;
dispatchingValue(null);
}
// 分发数据给观察者
private void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (initiator != null) {
considerNotify(initiator);
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
}
}
}
// 检查并通知观察者
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged(mData);
}
}
1.1.1 核心逻辑解析
- SafeIterableMap:
LiveData使用SafeIterableMap来管理观察者,这是一种线程安全的 Map,可以在迭代时进行修改。 - 版本控制:
mVersion用来跟踪数据的版本号,防止观察者重复接收相同的数据更新。 - ObserverWrapper:
ObserverWrapper是对观察者的包装类,确保观察者的生命周期感知。下面是ObserverWrapper的部分实现:
java
复制代码
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
abstract boolean shouldBeActive();
boolean activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return false;
}
mActive = newActive;
return true;
}
}
- LifecycleBoundObserver:
LifecycleBoundObserver继承自ObserverWrapper,并重写了shouldBeActive方法,基于LifecycleOwner的状态来判断观察者是否活跃:
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(Lifecycle.State.STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
}