ViewModel怎么保存数据

2,458 阅读3分钟
ViewModel注重生命周期,核心功能:屏幕旋转时可以保存数据, 实现Fragment与Activity,以及Fragment与Fragment之间的数据通信与共享。

总结一下 ViewModel的作用:1. 当容器用来管理数据  2. 管理生命周期发生变化 当配置发生变化的时候保证数据不会丢失 比如屏幕的旋转。

获取ViewModel的代码:

studentViewModel= ViewModelProviders.of(this).get(StudentViewModel.class);

ViewModelProviders.of(this)一执行完 ViewModel 就拿到了,通过get方法把我们自己写的 VeiwModel(StudentViewModel)拿到,下面进入get方法的源码:

反射字节码

反射字节码得到StudentViewModel的对象

ViewModelProvider.java

public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        ......
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {

    //从 ViewModelStore里面去获取,ViewModelStore就是一个HashMap
    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
    } else {
        //使用工厂的 create 方法拿到 一个 ViewModel
        viewModel = (mFactory).create(modelClass);
    }
    mViewModelStore.put(key, viewModel);//将viewModel put进ViewModelStore
    return (T) viewModel;
}

上面的create方法是ViewModelProvider里的一个接口方法

public interface Factory {
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

接口的实现在 ViewModelProvider的内部类AndroidViewModelFactory 

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
    if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
        //noinspection TryWithIdenticalCatches
        try {
            //通过反射来实现
            return modelClass.getConstructor(Application.class).newInstance(mApplication);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
          ......

旋转屏幕保存数据的原理

ViewModelProviders.java

@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
    return of(activity, null);
}
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
        @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

最后在new出一个ViewModelProvider时调用了getViewModelStore()方法,我们来看一下

ComponentActivity.java

@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.");
    }
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
               //操作系统会调用该方法,屏幕只要已转这个方法就会被执行
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        //注意:第一次调用getViewModelStore时,mViewModelStore一定为空
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

屏幕一转动首先调用的是 ComponentActivity 中的方法 onRetainCustomNonConfigurationInstance() 

在转动的过程中以下两个方法会被调用 :

1. onRetainNonConfigurationInstance  2.getLastNonConfigurationInstance

public final Object onRetainNonConfigurationInstance() {
     //该方法返回空,用户可以重写此方法
    Object custom = onRetainCustomNonConfigurationInstance();

    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        // No one called getViewModelStore(), so see if there was an existing
        // ViewModelStore from our last NonConfigurationInstance
        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返回一个 nci的对象,nci对象会存储一个 ViewModelStore,而ViewModel是放在 ViewModelStore里面的。

NonConfigurationInstances是ComponentActivity 的一个内部类

static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;//ViewModel存放在ViewModelStore里面
}

所以 Activity 保存了一个  NonConfigurationInstances  的数据结构,在我们旋转屏幕时,会通过 getLastNonConfigurationInstance 方法先从activity中获取 ViewModelStore, 如果activity中不存在就会 new 一个 NonConfigurationInstances,把一个ViewModelStore存储进去,下次getLastNonConfigurationInstance时就可以直接获取 所以不管怎么翻转屏幕,数据只有一份,来看一下这个方法

Activity.java

public Object getLastNonConfigurationInstance() {
    //该方法有数据就会返回一个 activity 没有数据就会返回空
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

每一个 Activity 都会有一个 NonConfigurationInstances 的数据结构

Activity.java

NonConfigurationInstances retainNonConfigurationInstances() {
    //在 Activity中调用了 onRetainNonConfigurationInstance 方法
    Object activity = onRetainNonConfigurationInstance();
    HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
    ......
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
......
return nci;
}