一般用法
val viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(TestViewModel::class.java)
ViewModelProvider 的构造函数给 mFactory 和 mViewModelStore 进行了赋值,如果使用只含有一个参数的构造函数,并且这个 ViewModelStoreOwner 类型是 HasDefaultViewModelProviderFactory 类型的,则会通过它的 getDefaultViewModelProviderFactory() 函数获取默认的 mFactory 对象,否则为 NewInstanceFactory 类型的对象。 对于 AppCompatActivity 来说,mFactory 是一个 SavedStateViewModelFactory 对象,这是在它的间接父类 ComponentActivity 中定义的。
/*ViewModelProvider.java*/
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
ComponentActivity 实现了 HasDefaultViewModelProviderFactory 和 ViewModelStoreOwner 接口。ComponentActivity 的 getViewModelStore 会尝试从上一个 NonConfigurationInstances 中恢复 ViewModelStore,这也是 Activity 屏幕旋转导致 Activity 销毁重建后,ViewModel 能自行恢复的原因。
/*ComponentActivity.java*/
@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;
}
get 的实现很简单,先从 mViewModelStore 试图恢复已存在的 ViewModel 类型,否则调用 mFactory 的 create 创建新的 ViewModel。
/*ViewModelProvider.java*/
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
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;
}
在 ViewModelProvider 的内部类 NewInstanceFactory 中, create 也只是仅仅 new 了一下对象
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
如果你认为 ViewModel 的源码就这么简单,仅仅只是从 ViewModelProvider 中取得对应的 ViewModel 对象来进行后续任务的处理。那就是 too young too simple。
官方说 ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续存在。这是如何办到的呢?那就看看 NonConfigurationInstances 有啥奥秘。
/*Activity.java*/
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
mLastNonConfigurationInstances 在 attach() 被赋值,在 performResume() 时被清空,attach() 为系统启动 Activity 时由 handleLaunchActivity( ) 调用,根据 Activity 启动流程, 我们跳过前面步骤,直接从 ActivityThread 的 performLaunchActivity() 开始跟踪 NonConfigurationInstances 是怎么创建和保留的。
/*Activity.java*/
@UnsupportedAppUsage
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
// attachBaseContext 回调
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
// 创建 PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
... 省略了一些初始化逻辑
// lastNonConfigurationInstances 在这里被传入了
mLastNonConfigurationInstances = lastNonConfigurationInstances;
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
...
}
[ 源码来源为 Android 10 ]
我们从 ActivityThread 内部Handler H 收到 RELAUNCH_ACTIVITY 信号,开始处理 Activity 启动流程开始跟踪
case RELAUNCH_ACTIVITY:
handleRelaunchActivityLocally((IBinder) msg.obj);
break;
/*ActivityThread.java*/
private void handleRelaunchActivityLocally(IBinder token) {
final ActivityClientRecord r = mActivities.get(token);
final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain(
null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */,
mergedConfiguration, r.mPreserveWindow);
final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
transaction.addCallback(activityRelaunchItem);
executeTransaction(transaction);
}
executeTransaction 间接调用了 ClientTransactionHandler.handleRelaunchActivity() , 所以还是回到 ActivityThread 的 handleRelaunchActivity 方法。
handleRelaunchActivity 从 mActivities 取出 ActivityClientRecord 对象 r,作为参数传给了 handleRelaunchActivityInner 去处理。
/*ActivityThread.java*/
@UnsupportedAppUsage
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
@Override
public void handleRelaunchActivity(ActivityClientRecord tmp,
PendingTransactionActions pendingActions) {
...
ActivityClientRecord r = mActivities.get(tmp.token);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handling relaunch of " + r);
if (r == null) {
return;
}
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
}
handleRelaunchActivityInner 处理了 Activity 启动要准备的一些前提工作,然后就将任务交给 handleLaunchActivity 了。
/*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) {
performPauseActivity(r, false, reason, null /* pendingActions */);
}
if (!r.stopped) {
callActivityOnStop(r, true /* saveState */, reason);
}
handleDestroyActivity(r.token, false, configChanges, true, reason);
...
handleLaunchActivity(r, pendingActions, customIntent);
}
handleLaunchActivity 调用 performLaunchActivity 来真正启动 Activity, 如果启动失败,就直接通知 AMS finish 掉这个任务。源码中这种处理还挺常见的,失败了就干掉它。
/*ActivityThread.java*/
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler(); // 移出 GcIdler
mSomeActivitiesChanged = true;
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
WindowManagerGlobal.initialize();
// Hint the GraphicsEnvironment that an activity is launching on the process.
GraphicsEnvironment.hintActivityLaunch();
final Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
try {
ActivityTaskManager.getService()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
return a;
}
/*ActivityThread.java*/
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
...
// 这里 new 了 ContextImpl 对象
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
// Instrumentation 新建 Activity 对象
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
...
appContext.setOuterContext(activity);
// 这里调用了 Activity.java 的 attach() 方法
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);
...
// activity 设置了 主题,更新主题
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
// Activity OnCreate 的回调
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
}
r.setState(ON_CREATE);
// Activity 启动成功,会将 ActivityClientRecord 缓存下来
synchronized (mResourcesManager) {
mActivities.put(r.token, r);
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
所以,一切都变得清晰了。NonConfigurationInstances 是保存在进程空间的,所以旋转屏幕 Activity 重建,只要IBinder 对象是同一个,也能从 mActivities 拿到保存的记录。 在这里,IBinder 是一个进程唯一的 IApplicationThread 类型,用来与 WMS 等通信的。虽然屏幕旋转导致 Activity 重建了,但是进程没有被销毁重建,从而得以保证 NonConfigurationInstances 记录可以取得。那么,就算是新建的 Activity 也是可以拿到销毁之前的 ViewModel 数据。
由此也可以得到为什么使用 ViewModel 时候,绝不能引用视图、Lifecycle 或可能存储对 Activity 上下文的引用的任何类 ,主要是为要避免内存泄漏。如果必须要在 ViewModel 中使用 Context,可以使用 Application 的 Context,通过 ViewModelProvider.AndroidViewModelFactory 构造的 AndroidViewModel 可以满足这一需求。当前,你也可以发挥你的创意,构造属于你自己的 ViewModelProvider.Factory。
UserModel model = new ViewModelProvider(activity, new ViewModelProvider.AndroidViewModelFactory(BaseApplication.getAppInstance())).get(UserModel.class);
Kotin 可以通过 by 关键字简化这一动作
val viewModel: UserModel by viewModels()
class UserModel(@get:JvmName("getContext_") val application: Application) : AndroidViewModel(application) {}