Android Jetpack开发:原理解析与应用实战
上QQ阅读APP看书,第一时间看更新

3.5 原理小课堂

在3.3节中,已经分析了ViewModelProvider(this).get的内部方法,我们从中了解到创建的ViewModel对象都会被存在ViewModelStore中,如果创建的ViewModel对象已存在,则直接取出对象并返回,如果不存在则新建。

ViewModelProvider(this)中this的参数类型是ViewModelStoreOwner,由于在androidx.activity.ComponentActivity中实现了ViewModelStoreOwner接口并实现了getViewModelStore方法,因此开发者可以直接使用当前Activity的上下文this,主要代码如下:

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ViewModelStoreOwner{
    ...
    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                + "Application instance. You can't request ViewModel before onCreate call.");
        }
        ensureViewModelStore();
        return mViewModelStore;
    }
    ...
}

接着来看getViewModelStore方法,此方法会进入ensureViewModelStore方法中,ensureViewModelStore方法的代码如下:

    void ensureViewModelStore() {
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}

这里通过(NonConfigurationInstances) getLastNonConfigurationInstance()方法来获取上一次的配置信息,NonConfigurationInstances中有viewModelStore对象,代码如下:

    static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
}

如果上一次配置信息不为空,就直接使用上一次的viewModelStore,如果为空则新建viewModelStore。在Activity旋转屏幕被销毁的时候,不仅会调用onSaveInstance-State方法,而且会调用onRetainNonConfigurationInstance方法,onRetainNonConfiguration-Instance方法的代码如下:

public final Object onRetainNonConfigurationInstance() {
    Object custom = onRetainCustomNonConfigurationInstance();
    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        NonConfigurationInstances nc =
        (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }
    if (viewModelStore == null && custom == null) {
        return null;
    }
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

可以看到,在onRetainNonConfigurationInstance方法中对viewModelStore进行了存储。当屏幕旋转恢复的时候会通过getLastNonConfigurationInstance方法进行恢复。getLastNonConfigurationInstance方法的代码如下:

@Nullable
public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
        ? mLastNonConfigurationInstances.activity : null;
}

所以在Activity旋转屏幕的整个过程中,ViewModelStore对象保留了下来,通过ViewModelProvider(this).get方法获取到的是同一个ViewModel实例,从而避免了由于屏幕旋转而导致数据丢失的问题。

前面也提到了ViewModel虽然可以防止屏幕旋转引起的数据丢失,但ViewModel并不能代替onSaveInstanceState方法,主要原因有如下两点:

  • onSaveInstanceState方法可以存储少量的序列化数据,ViewModel可以存储任意数据,只是使用时的限制不同。
  • onSaveInstanceState可以达到数据持久化的目的,但是ViewModel不可以,使用场景不同。

为什么说ViewModel不能达到数据持久化的目的呢?因为当Activity被真正销毁的时候,ViewModel会将资源进行回收,示例代码如下:

    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();
            }
        }
    }
});

从上面代码可以看出,当对应的Activity被真正销毁,即不是屏幕旋转导致页面被销毁时,viewModelStore将会调用clear方法清理数据,所以ViewModel并不能达到数据持久化的目的。

可见,ViewModel并不能替代onSaveInstanceState方法,在实际开发中,读者应选择适合自己业务的方法,从而达到最佳体验。