深入理解 ViewModel和LiveData的工作原理

235 阅读8分钟

ViewModel 是 Android Jetpack 架构组件中的核心部分,旨在以生命周期感知的方式存储和管理与 UI 相关的数据。它通过保持数据的持久性,避免了配置更改(如屏幕旋转)导致的数据丢失。下面详细解释 ViewModel 的工作原理、使用到的设计模式及其实际使用方法。

一、ViewModel 工作原理

1. 生命周期感知和状态管理

ViewModel 是生命周期感知的,它的生命周期独立于 ActivityFragment,因此在配置更改(如屏幕旋转)时,ViewModel 实例不会被销毁。这意味着在这些配置更改期间,数据可以保持不变,从而避免了不必要的数据重新加载和计算。

ViewModel的初始化和获取

ViewModelProvider

ViewModelProvider 是用来创建和获取 ViewModel 实例的工具类。它通过 ViewModelStore 来存储 ViewModel 实例,使其与 ActivityFragment 的生命周期绑定。

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 中是否已经存在具有指定 keyViewModel 实例,如果存在且类型匹配,则直接返回;否则,会通过 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 被清除时(如 ActivityFragment 被销毁时),存储的 ViewModel 实例也会被清除,并调用它们的 onCleared() 方法。

ViewModel的生命周期管理

ViewModel 是生命周期感知的,这意味着它会在 ActivityFragment 被销毁时释放资源。ViewModel 的核心生命周期管理逻辑体现在 ViewModelStoreclear() 方法中,当宿主(ActivityFragment)的生命周期结束时,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 的实例对象管理机制主要依赖于以下几个关键点:

  1. ViewModelProvider: 负责获取和创建 ViewModel 实例,并与 ViewModelStore 进行交互。
  2. ViewModelStore: 负责存储 ViewModel 实例,并管理其生命周期。
  3. 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 是与 ActivityFragmentViewModelStoreOwner 相关联的。关键在于,ViewModelStore 是在 ActivityonRetainNonConfigurationInstance() 方法中保持的,并且与新的 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对象中,最终与ActivityThreadActivity的生命周期管理相关联。以下是简要说明:

  • ActivityThread中的管理ActivityThread是 Android 应用程序的主线程,负责管理应用程序的所有活动(Activity)等组件的生命周期。在ActivityThread中,当处理 Activity 的创建、恢复等操作时,会涉及到nonConfigurationInstance的处理。例如,在ActivityThreadperformCreate等方法中,会根据是否存在已保存的nonConfigurationInstance来恢复 Activity 的相关状态。
  • ActivityClientRecord的作用ActivityClientRecordActivityThread内部用于记录 Activity 相关信息的一个类。它存储了与一个 Activity 实例相关的各种数据和状态,包括nonConfigurationInstance。当 Activity 需要保存其非配置相关的状态时,相关数据会被封装到nonConfigurationInstance中,并存储在ActivityClientRecord里。比如,在 Activity 由于配置变化(如屏幕旋转)而重新创建时,ActivityClientRecord中的nonConfigurationInstance就可以用来传递和恢复之前 Activity 保存的 ViewModel 等重要数据和状态信息。
  • Activity的关联Activity通过ActivityThreadActivityClientRecord间接与nonConfigurationInstance关联。在ActivityonRetainNonConfigurationInstance方法中,可以将需要保存的对象(如包含ViewModelStorenonConfigurationInstance)返回,以便在 Activity 重建时恢复。而在ActivityonCreate等方法中,可以通过getLastNonConfigurationInstance等方法获取nonConfigurationInstance,从而恢复之前保存的状态。

另外,在 Android 的更新中,可以通过 Kotlin 的委托属性来更简洁地初始化 ViewModel,不再需要手动使用 ViewModelProvider

  • by viewModels() :用于在 FragmentActivity 中创建或获取与该组件生命周期绑定的 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());
    }
}