写在前面:Jetpack的更新速度非常快,可能你一个月前看WorkManager是这样用的,下个月这个使用方法就有可能被废弃了(我看源码的时候是遇到过的,而且源码也变了,但核心原理是不变的),所以我们这一系列文章偏重讲原理,使用就一带而过(因为讲了也没用啊,会变的。。。。。,读者使用最好看官方文档官方文档
,当然我这里讲的也是截止到目前的最新用法)。
Jetpack使用(一)Lifecycles核心原理
Jetpack使用(二)LiveData核心原理
Jetpack使用(三)DataBinding核心原理
Jetpack使用(四)ViewModel核心原理
Jetpack使用(五)Navigation核心原理
Jetpack使用(六) WorkManager的4种用法
ViewModel其实是和前面说的LiveData一起组合使用的,引用官方的一句话:ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续存在。也就是如果使用了ViewModel,那么在屏幕旋转的时候,我们不用再使用 onSaveInstanceState() 方法从 onCreate() 中的恢复数据,其实已经在onRetainNonConfigurationInstance里帮我们做了保存机制。
使用
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<User>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
看过前面三篇文章的话,应该对于observe这些观察者代码很了解,所以我们只源码分析MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);这一句代码,来看看到底是怎么实现屏幕旋转保存数据的。
核心原理
我们先进入ViewModelProviders.of(this)。of方法看看
@NonNull
@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);
}
这段代码的意思就是通过getInstance方法返回一个AndroidViewModelFactory传进去,返回一个ViewModelProvider,所以我们看看activity.getViewModelStore()到底是怎么实现的
@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.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
mViewModelStore是一个ViewModelStore类型的变量,是ComponentActivity的成员变量,如果mViewModelStore为null,就先新建一个NonConfigurationInstances对象nc,我们看看getLastNonConfigurationInstance到底是什么
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
从上面代码可以看出,nc里就是保存了actvity或者fragment的一些屏幕旋转时的数据信息的,所以如果mViewModelStore等于空,他会把新建一个mViewModelStore,并把actvity或者fragment的信息存储进去了,of分析完了,我们再回到前面,看看get(StudentViewModel.class);是怎么实现的
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
一直点进去最后到这里,发现最终是通过create方法生成viewModel的,create方法一直进去就是通过反射生成viewModel的,最后把生成的viewModel放进
mViewModelStore里。
最后再看到ComponentActivity里屏幕旋转时要调用到的方法
@Override
@Nullable
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;
}