先看使用方法:
public class MyActivity extends AppCompatActivity {
private MyViewModel vm;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyActivityBinding binding = MyActivityBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
vm = new ViewModelProvider(this,new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
binding.btSend.setOnClickListener(v -> {
vm.setAccount("123456789");
});
binding.btGet.setOnClickListener(v->{
Log.e("ViewModel",vm.mAccount.getValue().toString());
});
vm.mAccount.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.e("ViewModel","Observer--onChanged" + s);
}
});
}
}
public class MyViewModel extends AndroidViewModel implements Serializable {
public MyLiveData<String> mAccount;
public MyViewModel(@NonNull Application application) {
super(application);
mAccount = new MyLiveData<>();
}
public void setAccount(String account){
mAccount.setValue(account);
}
}
上面这段代码用到了ViewBinding、去除粘性的LiveData、ViewModel和LiveData连用
这篇讲ViewModel:
先提几个问题
1.ViewModel以什么形式存在
ViewModel就是一个工具类,我们可以把它当成MVP中的Presenter,可以在里面处理一些网络请求的逻辑维护一些数据
2.ViewModel的作用
在横竖屏切换Activity重建的时候ViewModel中的数据还能保存下来
3.为啥ViewModel能够在横竖屏切换Activity重建的时候ViewModel中的数据还能保存下来
首先介绍一个类:ViewModelStore
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);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
这个类很显然就是系统来帮我们维护我们创建的ViewModel的,用Map维护有存有取有清空
那具体是什么时候存什么时候取什么时候清空?
再介绍一个接口:ViewModelStoreOwner
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
接口的实质就是,实现了这个接口就有了这个接口中方法的能力,我们的ComponentActivity实现了这个借口并且实现了getViewModelStore方法ComponentActivity就具备了获取ViewModelStore的能力。
@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;
}
这段代码需要注意下:
NonConfigurationInstances nc = (NonConfigurationInstances)getLastNonConfigurationInstance();
这一句,想完全搞清楚这个东西的来历和作用得看AMS的源码,所以这里大家就先记住这个NonConfigurationInstances保存了viewModelStore,viewModelStore保存了ViewModel,NonConfigurationInstances是AMS在上次横竖屏切换时AMS保存下来的,也正是因为这个NonConfigurationInstances使我们的ViewModel能够在横竖屏切换Activity重建之后还能保存之前的数据。
下面开始分析ViewModel具体是什么时候被viewModelStore存什么时候取什么时候清空?
存、取:
在获取ViewModel的时候:
ViewModel vm = new ViewModelProvider(this,new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
Activity实现了ViewModelStoreOwner这个接口,把Activity的this当作参数传入,这个this其实就是ViewModelStoreOwner这个接口的实现类,上面代码中owner.getViewModelStore()就获取了ViewModelStore并传入
这时mViewModelStore就维护在了ViewModelProvider中,我们再看活去ViewModel这句:
ViewModel vm = new ViewModelProvider(this,new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
这个get(Class)方法:参数就是我们已经定义好的ViewModel的Class在这里是MyViewModel.class
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(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 {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
这个就是通过反射来获取ViewModel的实例并通过ViewModelProvider中的mViewModelStore保存起来,这个ViewModelProvider中的mViewModelStore是通过Activity的this调用getViewModelStore方法获取的,而this是通过参数传到ViewModelProvider中的,我们知道如果参数是指针类型那在ViewModelProvider中改变这个指针中的内容那只要是同一个指针,其内容都会随之改变,所以反射得到的viewModel就被保存在mViewModelStore的map中了。
清空:
在ComponentActivity的构造函数中:
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();
}
}
}
});
注意这个判断if (!isChangingConfigurations()),isChangingConfigurations()这个方法:
public boolean isChangingConfigurations() {
return mChangingConfigurations;
}
mChangingConfigurations这个flag的意义是关键。默认是false
现在已知发生横竖屏切换的时候mChangingConfigurations就是true,所以当发生横竖屏切换的时候mViewModelStore中的viewModel不会被清空,当正常退出页面的时候才会。
现在知道了viewModel在获取的时候就被维护在了Activity的mViewModelStore中,横竖屏切换不会清除mViewModelStore中的ViewModel,只有正常退出才会清除,还有一个问题,现在只是没有清除mViewModelStore中的ViewModel,当横竖屏切换的时候Activity会重建mViewModelStore又是如何保存的呢?
在ComponentActivity中有这样一个方法:
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;
}
在配置更改时会调用 Activity#
onRetainNonConfigurationInstance() 来保存保存着 ViewModel 示例的对象 mViewModelStore,并在 Activity 重建后调用 getViewModelStore() ,在getViewModelStore()内部会调用getLastNonConfigurationInstance() 方法获取是否有缓存的 ViewModelStore 对象,若有则返回,没有则创建新 ViewModelStore 实例
这个onRetainNonConfigurationInstance()是怎么调用的呢?
请看下篇:
AndroidFramwork源码解析onRetainNonConfigurationInstance()调用逻辑