问题思考
- 为啥要用viewmodel呢?
1. viewmodel有啥用
用大白话说,保证数据稳定性,以下面例子举例
class MainActivity : AppCompatActivity() {
private val Tag = "jetpack-->"
private val mVm = MainVm()
// private val mVm: MainVm by lazy {
// ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MainVm::class.java)
// }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(Tag, "onCreate: ")
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.mainVm = mVm
}
fun onClick(view: View) {
mVm.count.set((mVm.count.get() ?: 0) + 1)
}
override fun onRetainCustomNonConfigurationInstance(): Any? {
Log.d(Tag, "onRetainCustomNonConfigurationInstance: ")
return super.onRetainCustomNonConfigurationInstance()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Log.d(Tag, "onSaveInstanceState(outState)")
}
override fun onDestroy() {
super.onDestroy()
Log.d(Tag, "onDestroy: ")
}
}
class MainVm: ViewModel() {
var count = ObservableField(0)
}
// xml 布局
<Button
android:id="@+id/btn_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@{mainVm.count+``}" />
上面是一个页面只有一个button的布局,如果我们mVm是直接new出来,那么他失去了viewmodel原本作用,官方写法是通过ViewModelProvider创建。以上例子我们不断点击按钮会自增,当旋转屏幕的时候,原来Activity 会被销毁,并重新创建,同时mainVm.count也会重置=0。
当我们用官方提供的写法,使用ViewModelProvider创建viewmodel,那么屏幕旋转的时候也是会保持mainVm.count原有数据
// 使用val mVm = MainVm() 的旋转屏幕日志
2023-02-20 14:34:17.762 28617-28617/com.wqg.wqgdatabdingandviewmodel D/jetpack-->: onSaveInstanceState(outState) mVm.count=5
2023-02-20 14:34:17.762 28617-28617/com.wqg.wqgdatabdingandviewmodel D/jetpack-->: onRetainCustomNonConfigurationInstance: mVm.count=5
2023-02-20 14:34:17.763 28617-28617/com.wqg.wqgdatabdingandviewmodel D/jetpack-->: onDestroy:
2023-02-20 14:34:17.785 28617-28617/com.wqg.wqgdatabdingandviewmodel D/jetpack-->: onCreate: mVm.count=0
使用new 生成的viewmodel,onCreate 的时候会重新新建对象
// 使用官方ViewModelProvider创建MainVM 旋转屏幕日志
2023-02-20 14:35:26.263 29036-29036/com.wqg.wqgdatabdingandviewmodel D/jetpack-->: onSaveInstanceState(outState) mVm.count=6
2023-02-20 14:35:26.263 29036-29036/com.wqg.wqgdatabdingandviewmodel D/jetpack-->: onRetainCustomNonConfigurationInstance: mVm.count=6
2023-02-20 14:35:26.265 29036-29036/com.wqg.wqgdatabdingandviewmodel D/jetpack-->: onDestroy:
2023-02-20 14:35:26.286 29036-29036/com.wqg.wqgdatabdingandviewmodel D/jetpack-->: onCreate: mVm.count=6
使用viewModelProvider创建的viewmodel, onCreate 的时候还是原来数据
2. viewmodel它是怎么实现这个
可以看到我们切换屏幕是会调用onRetainCustomNonConfigurationInstance这个方法,直接中文式翻译『保留自定义非配置实例』,反查可以看到Activity的onRetainNonConfigurationInstance调用该方法,来看看
Activity.java
public final Object onRetainNonConfigurationInstance() {
// 自定义的非实例
Object custom = onRetainCustomNonConfigurationInstance();
// 系统默认的
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// 可以看到,会获取最近的『非配置实例』,nc
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// 从nc 获取这个viewModelStore
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
// 系统的viewModelStore和自定义的都存放到『非配置实例』里面
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
以上是说明了我们自定义的实例对象和系统的viewModelStore存放到了nc里面了。怎么触发这个鬼东西呢?主要还是要看我们页面重启,那我们再从ActivityThread看看我们怎么调用onRetainNonConfigurationInstance。 横竖屏会调用ActivityThread的handleRelaunchActivity(),看名字你也能发现是重启,里面又有一个handleRelaunchActivityInner()
ActivityThread.java
private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
PendingTransactionActions pendingActions, boolean startsNotResumed,
Configuration overrideConfig, String reason) {
// Preserve last used intent, it may be set from Activity#setIntent().
final Intent customIntent = r.activity.mIntent;
// Need to ensure state is saved.
if (!r.paused) {
// 进入了pause了
performPauseActivity(r, false, reason, null /* pendingActions */);
}
if (!r.stopped) {
// 进入了onStop
callActivityOnStop(r, true /* saveState */, reason);
}
// 重点看这个,既然要销毁,那再销毁之前肯定要保存
handleDestroyActivity(r, false, configChanges, true, reason);
r.activity = null;
r.window = null;
r.hideForNow = false;
r.nextIdle = null;
// Merge any pending results and pending intents; don't just replace them
if (pendingResults != null) {
if (r.pendingResults == null) {
r.pendingResults = pendingResults;
} else {
r.pendingResults.addAll(pendingResults);
}
}
if (pendingIntents != null) {
if (r.pendingIntents == null) {
r.pendingIntents = pendingIntents;
} else {
r.pendingIntents.addAll(pendingIntents);
}
}
r.startsNotResumed = startsNotResumed;
r.overrideConfig = overrideConfig;
// 启动activity
handleLaunchActivity(r, pendingActions, customIntent);
}
@Override
public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
// 核心代码
performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason);
cleanUpPendingRemoveWindows(r, finishing);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
if (v != null) {
if (r.activity.mVisibleFromServer) {
mNumVisibleActivities--;
}
IBinder wtoken = v.getWindowToken();
if (r.activity.mWindowAdded) {
if (r.mPreserveWindow) {
r.mPendingRemoveWindow = r.window;
r.mPendingRemoveWindowManager = wm;
r.window.clearContentView();
} else {
final ViewRootImpl viewRoot = v.getViewRootImpl();
if (viewRoot != null) {
viewRoot.setActivityConfigCallback(null);
}
wm.removeViewImmediate(v);
}
}
if (wtoken != null && r.mPendingRemoveWindow == null) {
WindowManagerGlobal.getInstance().closeAll(wtoken,
r.activity.getClass().getName(), "Activity");
} else if (r.mPendingRemoveWindow != null) {
WindowManagerGlobal.getInstance().closeAllExceptView(r.token, v,
r.activity.getClass().getName(), "Activity");
}
r.activity.mDecor = null;
}
if (r.mPendingRemoveWindow == null) {
WindowManagerGlobal.getInstance().closeAll(r.token,
r.activity.getClass().getName(), "Activity");
}
Context c = r.activity.getBaseContext();
if (c instanceof ContextImpl) {
((ContextImpl) c).scheduleFinalCleanup(r.activity.getClass().getName(), "Activity");
}
if (finishing) {
ActivityClient.getInstance().activityDestroyed(r.token);
}
mSomeActivitiesChanged = true;
}
void performDestroyActivity(ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
}
performPauseActivityIfNeeded(r, "destroy");
if (!r.stopped) {
callActivityOnStop(r, false /* saveState */, "destroy");
}
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);
}
}
}
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnDestroy(r.activity);
if (!r.activity.mCalled) {
throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent)
+ " did not call through to super.onDestroy()");
}
if (r.window != null) {
r.window.closeAllPanels();
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException("Unable to destroy activity "
+ safeToComponentShortString(r.intent) + ": " + e.toString(), e);
}
}
r.setState(ON_DESTROY);
mLastReportedWindowingMode.remove(r.activity.getActivityToken());
schedulePurgeIdler();
synchronized (this) {
if (mSplashScreenGlobal != null) {
mSplashScreenGlobal.tokenDestroyed(r.token);
}
}
// updatePendingActivityConfiguration() reads from mActivities to update
// ActivityClientRecord which runs in a different thread. Protect modifications to
// mActivities to avoid race.
synchronized (mResourcesManager) {
mActivities.remove(r.token);
}
StrictMode.decrementExpectedActivityCount(activityClass);
}
可以从上面看到handleRelaunchActivityInner->handleDestroyActivity->performDestroyActivity->r.activity.retainNonConfigurationInstances();。终于找到了。直白说就是销毁的时候会想办法把viewmodel存放到『自定义非配置实例』,新建的activity的时候直接从上一次存放的『自定义非配置实例』取对应的内容。