一.ViewModel的作用:
activity重建可能需要在销毁前保存变量数据,等重建完后将数据恢复; 现在可通过使用ViewModel替代此功能
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
//从bundle中获取变量
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
//将变量值存储到bundle中
}
二.ViewModel的使用 (旋转后ui上显示的值大小不发生改变)
步骤一: 创建一个类继承ViewModel, 定一个一个变量,用来测试横竖屏切换,变量值是否会发生改变
class RotateViewModel:ViewModel() {
var num = 0 //初始值
}
步骤二:创建ViewModel对象,点击改变num值,观察横竖屏的变量变化和 activity的hashcode和 viewModel对象的hashcode
class RotateActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my_activity)
var model = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
.get(RotateViewModel::class.java)
var btn = findViewById<Button>(R.id.btn_center)
btn.text = "${model.num}"
btn.setOnClickListener {
btn.text = "${++model.num}"
}
Log.i(TAG, "onCreate--- activity hashcode=${this.hashCode()}")
Log.i(TAG, "onCreate--- model hashcode=${model.hashCode()}")
}
override fun onRetainCustomNonConfigurationInstance(): Any? {
val cf = resources.configuration; //获取设置的配置信息
val ori = cf.orientation //获取屏幕方向
if(ori == Configuration.ORIENTATION_LANDSCAPE){
//横屏
Log.i(TAG, "横屏--- onRetainCustomNonConfigurationInstance-- ")
}else if(ori == Configuration.ORIENTATION_PORTRAIT){
//竖屏
Log.i(TAG, "竖屏--- onRetainCustomNonConfigurationInstance-- ")
}
return super.onRetainCustomNonConfigurationInstance()
}
override fun onDestroy() {
Log.i(TAG, "onDestroy--- activity hashcode=${this.hashCode()}")
super.onDestroy()
}
companion object {
const val TAG = "RotateViewModel"
}
}
运行后自由旋转,点击按钮num自增, 横竖屏ui显示的num值一样, 证明了即使activity发生重建, ViewModel中变量的值没有改变。观察切换屏幕的打印
//第一次打印 (竖屏)
RotateViewModel: onCreate--- activity hashcode=134750604
RotateViewModel: onCreate--- model hashcode=5104856
//第二次打印(切换到横屏)
竖屏--- onRetainCustomNonConfigurationInstance--
onDestroy--- activity hashcode=134750604
onCreate--- activity hashcode=254609558
onCreate--- model hashcode=5104856
//第三次打印(切换到竖屏)
横屏--- onRetainCustomNonConfigurationInstance--
onDestroy--- activity hashcode=254609558
onCreate--- activity hashcode=214362739
onCreate--- model hashcode=5104856
说明了ViewModel的生命周期要比Activity长, 即使Activity onDestory,ViewModel对象还是当初创建的ViewModel对象。有2个疑问: ViewModel生命周期为什么那么长? ViewModel何时被销毁?

三.ViewModel的原理
ViewModel自始至终离不开ViewModelStore, 分两种情况: ViewModel实例初始化和从ViewModelStore中获取Vie wModel
3.1 ViewModel实例初始化, 作为value存入ViewModelStore的缓存中
ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
.get(RotateViewModel::class.java)
这里的this 是ViewModelStoreOwner,ComponentActivity 实现 ViewModelStoreOwner并重写了getViewModelStore方法, 目的是为了得到ViewModelStore实例
public ViewModelStore getViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
//ViewModel还未创建时走这里
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
通过工厂模式创建ViewModel实例, 并将该实例存入mViewModelStore中
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;
}
这样我们就可以使用ViewModel了, 当我们切换横竖屏之后
3.2 从ViewModelStore的缓存中获取ViewModel实例 切换横竖屏, ActivityThread 会调用performDestroyActivity方法
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
/////////////////////省略/////////////////////
if (getNonConfigInstance) {
try {
//横竖屏切换
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to retain activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
/////////////////////省略/////////////////////
return r;
}
->Activity retainNonConfigurationInstances()
->ComponentActivity onRetainNonConfigurationInstance()
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;
}
//在Activity销毁前将viewModelStore保存到nci中
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
这一步的目的很明显了,在Activity销毁前将viewModelStore保存到nci中,nci会赋值给上一步Activity retainNonConfigurationInstances() 的局部变量activity
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
/////////////////////省略/////////////////////
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
if (mVoiceInteractor != null) {
mVoiceInteractor.retainInstance();
nci.voiceInteractor = mVoiceInteractor;
}
return nci;
}
最终会返回给 ActivityClientRecord的lastNonConfigurationInstances,这么做其实很明显了,即使activity 被销毁了(销毁的时候进行了横竖屏切换),ActivityClientRecord 始终保存着ViewModel实例
当我们从竖屏切换到横屏后,Activity重建,会重新执行生命周期的onCreate方法
var model = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
.get(RotateViewModel::class.java)
会再次得到viewModel实例,此时的实例依然是当初的ViewModel
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.");
}
//由于Activity重建了,此时ComponentActivity中的成员变量mViewModelStore默认是null
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
//从nc中获取到获取mViewModelStore,走这里
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
getLastNonConfigurationInstance() 是从哪得到的, activity重建时,ActivityThread会执行performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
/////////////////////省略/////////////////////
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
/////////////////////省略/////////////////////
return activity;
}
r.lastNonConfigurationInstances,这里的ActivityClientRecord是当初保持nc时的ActivityClientRecord实例, 因此此时的ViewModel是从mViewModelStore获取到的, 所以ViewModel被赋予了保存数据和恢复数据的功能
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/Fragment 被销毁,但不是屏幕横竖屏切换的情况下,ViewModel会被销毁