Viewmodel
总结:
viewmodel 在横竖屏切换的时候会被保存在ActivityThread里,在destroy的时候保存,在launcher的时候可以通过getlast…获取。
viewmodel 原理
- 1 AppCompatActivity 继承ComponentActivity 在Activity销毁时会调用这里,保存nci
- ComponentActivity
/**
* Retain all appropriate non-config state. You can NOT
* override this yourself! Use a {@link androidx.lifecycle.ViewModel} if you want to
* retain your own non config state.
*/
@Override
@Nullable
@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
// Maintain backward compatibility.
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; //zsg
}
- 2 在销毁的时候保存nci是从ActivityThread 中调过来的
- ActivityThread
- 保存到他的lastNonConfigurationInstances里
/** Core implementation of activity destroy call. */
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(); //zsg
} 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);
}
- 3 ActivityThread 在创建Activity的时候重新传入nci
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
appContext.getAttributionSource());
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
// updatePendingActivityConfiguration() reads from mActivities to update
// ActivityClientRecord which runs in a different thread. Protect modifications to
// mActivities to avoid race.
synchronized (mResourcesManager) {
mActivities.put(r.token, r);
}
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config =
new Configuration(mConfigurationController.getCompatConfiguration());
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
// Activity resources must be initialized with the same loaders as the
// application context.
appContext.getResources().addLoaders(
app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config, //zsg
r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
r.assistToken, r.shareableActivityToken);
- 4 ComponentActivity 中 调用getLastNonConfigurationInstance 拿nci的时候才有值
- Activity.java
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
- 5 在ViewModelProvider的构造方法中会先尝试从nci中拿viewModelStore,如果没有的话就创建viewModelStore
myViewModel = ViewModelProvider(this,ViewModelProvider.NewInstanceFactory())
.get(MyViewModel::class.java)
- ComponentActivity.java
@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
- 6 在viewmodelProvide.get的时候往viewmodelstore里放入数据
- 这里的store就是ViewModelProvider构造方法里创建的viewmodelstore
@Suppress("UNCHECKED_CAST")
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
val viewModel = store[key]
if (modelClass.isInstance(viewModel)) {
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
} else {
@Suppress("ControlFlowWithEmptyBody")
if (viewModel != null) {
// TODO: log a warning.
}
}
val extras = MutableCreationExtras(defaultCreationExtras)
extras[VIEW_MODEL_KEY] = key
// AGP has some desugaring issues associated with compileOnly dependencies so we need to
// fall back to the other create method to keep from crashing.
return try {
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
factory.create(modelClass)
}.also { store.put(key, it) } //zsg
}
一个viewmodel demo
- 1
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private var number: Int = 0
private lateinit var myViewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myViewModel = ViewModelProvider(this,ViewModelProvider.NewInstanceFactory())
.get(MyViewModel::class.java)
//zsg 必须要有这一句,如果只有click里的旋转后不能恢复数据
tv.text= "${myViewModel.number}"
Log.i("zsg", "onCreate:===== $savedInstanceState", Exception())
if (savedInstanceState != null) {
number = savedInstanceState.getInt("number")
tv1.text = number.toString()
}
bt.setOnClickListener {
tv.text = "${++myViewModel.number}"
number +=2
tv1.text = "${number}"
}
}
override fun onSaveInstanceState(outState: Bundle) {
Log.i("zsg", "onSaveInstanceState:===== ", Exception())
outState.putInt("number",number)
super.onSaveInstanceState(outState)
}
}
- 2
import androidx.lifecycle.ViewModel
class MyViewModel : ViewModel() {
var number:Int = 0
}
- 3 gradle
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
//zsg kotlin自带的绑定布局,加上后直接在代码里敲view的id就会弹出导包
id 'kotlin-android-extensions'
}