Jetpack-ViewModel(面试深度起来)

面试素质三联?

  • ViewModel优点是啥? 答:保存数据 自动管理。
  • Fragment能拿Activity的ViewModel么? 答:能吧?。 = = !
  • ViewModel怎么管理的? 答:母鸡。
  • ViewModel怎么创建的? 答:母鸡啊

结果

回去等消息吧,面试官顺手评价 一个深度不够,基础不牢。

逐步拆解

1
2
3
kotlin复制代码 private  val  model by lazy {
       ViewModelProvider(this).get(BaseViewModel::class.java);
  }

ViewModelProvider干了啥?

1
2
3
4
5
6
7
8
9
10
kotlin复制代码  public constructor(
       owner: ViewModelStoreOwner
  ) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))



//构建Factory
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
               if (owner is HasDefaultViewModelProviderFactory)
                   owner.defaultViewModelProviderFactory else instance

整体看了一下,大体就三件事, 用this的 viewModelStore,搞Factory, 搞参数。

this 里面搞大事。

原来ViewModelProvider需要传入一个ViewModelStoreOwner 接口

1
2
3
4
scss复制代码 
public constructor(
       owner: ViewModelStoreOwner
  ) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))

看下ViewModelStoreOwner 就一个方法,返回一个ViewModelStore

1
2
3
kotlin复制代码interface ViewModelStoreOwner {
   val viewModelStore: ViewModelStore
}

我们常用的ComponentActivity/Fragment都实现了ViewModelStoreOwnerHasDefaultViewModelProviderFactory ,我们看下他们的实现getViewModelStore()的区别。

  • ComponentActivity的实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
scss复制代码   @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.");
      }
       ensureViewModelStore();
       return mViewModelStore;
  }

  void ensureViewModelStore() {
       if (mViewModelStore == null) {
           NonConfigurationInstances nc =
                  (NonConfigurationInstances) getLastNonConfigurationInstance();
           if (nc != null) {

               mViewModelStore = nc.viewModelStore;
          }
           if (mViewModelStore == null) {
               mViewModelStore = new ViewModelStore();
          }
      }
  }

简略看下核心代码,getLastNonConfigurationInstance没有,就直接new一个返回。

ComponentActivity 还是比较简单的,相当于自己管自己。

  • Fragment的实现(版本 1.6.2)

1
2
3
4
5
6
less复制代码 @NonNull
   @Override
   public ViewModelStore getViewModelStore() {
      // 省略部分代码
       return mFragmentManager.getViewModelStore(this);
  }

Fragment 调用FragmentManagergetViewModelStore(),(下面简称Fm)并把自己传入进去了。

追一下看看 进入 Fm。

1
2
3
4
5
less复制代码 private FragmentManagerViewModel mNonConfig; 
@NonNull
   ViewModelStore getViewModelStore(@NonNull Fragment f) {
       return mNonConfig.getViewModelStore(f);
  }

FragmentManagerViewModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ini复制代码FragmentManagerViewModel extends ViewModel{
//省略部分代码
private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();
 @NonNull
   ViewModelStore getViewModelStore(@NonNull Fragment f) {
       ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
       if (viewModelStore == null) {
           viewModelStore = new ViewModelStore();
           mViewModelStores.put(f.mWho, viewModelStore);
      }
       return viewModelStore;
  }
 
  //省略部分代码
  }

FragmentManager 调用自己的mNonConfig,传入了FragmentmNonConfig 自己就是一个FragmentManagerViewModel。也是一个ViewModel

到这里我们理解Frament 其实委托给了FragmentManager

先看下Fm里的mNonConfig 咋来的。看初始化

FragmentManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
less复制代码

void attachController(@NonNull FragmentHostCallback<?> host,
@NonNull FragmentContainer container, @Nullable final Fragment parent) {
//省略部分代码

if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
}

mNonConfig.setIsStateSaved(isStateSaved());
// 设置setNonConfig()
mFragmentStore.setNonConfig(mNonConfig);

FragmentManagerViewModel

1
2
3
4
5
6
csharp复制代码@NonNull
static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
FACTORY);
return viewModelProvider.get(FragmentManagerViewModel.class);
}

我们解析三个创建

+ `if` 可以理解为`fragment`嵌套 ,去找`getChildNonConfig()`。
+ `else if` 取到`host`的`viewModelStore`,自己创建了一个 `ViewModelProvider` 缓存`FragmentManagerViewModel`类进去。
+ `else` 自己 host 取不到,自己创建一个`FragmentManagerViewModel`,传入`false`,表示不自动缓存。我们看下看下host的真面目

Fragment

1
2
3
4
csharp复制代码 void performAttach() {
//省略部分代码
mChildFragmentManager.attachController(mHost, createFragmentContainer(), this);
}

host 看下哪里初始化的

FragmentController

1
2
3
4
5
less复制代码
public void attachHost(@Nullable Fragment parent) {
mHost.mFragmentManager.attachController(
mHost, mHost /*container*/, parent);
}

再看下 这个mHost怎么初始化的

1
2
3
typescript复制代码public static FragmentController createController(FragmentHostCallback<?> callbacks) {
return new FragmentController(callbacks);
}

追一下 进入到了FragmentActivity

1
2
3
4
5
6
7
8
9
10
11
scala复制代码 final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

//内部类
class HostCallbacks extends FragmentHostCallback<FragmentActivity>{
// 省略部分代码
@NonNull
@Override
public ViewModelStore getViewModelStore() {
return FragmentActivity.this.getViewModelStore();
}
}

捋一下,Fragment里面的 mNonConfig.getViewModelStore(f)
最后会调用到 host .getViewModelStore()。而 host 是 FragmentActivity 里的一个内部类。 最后 调用到 FragmentActivity.this.getViewModelStore()。 而FragmentActivity继承了ComponentActivity

正常情况下来说,FragmentManager 调用的是 ComponentActivitygetViewModelStore(),拿到了 然后 构建了一个ViewModelProvider() 返回。

实在不行了,自己 创建一个 FragmentManagerViewModel(false)用于处理。

ViewModelStore是个啥?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
kotlin复制代码open class ViewModelStore {

private val map = mutableMapOf<String, ViewModel>()


@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun put(key: String, viewModel: ViewModel) {
val oldViewModel = map.put(key, viewModel)
oldViewModel?.onCleared()
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
operator fun get(key: String): ViewModel? {
return map[key]
}


@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun keys(): Set<String> {
return HashSet(map.keys)
}

fun clear() {
for (vm in map.values) {
vm.clear()
}
map.clear()
}
}

一眼明白,一个map,能存ViewModel。 能清除,无了。

ViewModelProvider. get() 又搞了啥?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
kotlin复制代码 @MainThread
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
val canonicalName = modelClass.canonicalName
?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
return get("$DEFAULT_KEY:$canonicalName", modelClass)
}

@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

return try {
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
factory.create(modelClass)
}.also { store.put(key, it) }
}

扫一眼,拿传入的类名 canonicalName 前面拼接 “DEFAULT_KEY”,如果当前store有,且是 是当前类的实例,就强转返回。 没有就factory 创建。最后调用 also缓存进去。看下 factory

前面我们知道ComponentActivityFragment实现了HasDefaultViewModelProviderFactory

  • ComponentActivity
1
2
3
4
5
6
7
8
9
10
11
less复制代码 @NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
  • Fragment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ini复制代码   @NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
if (mDefaultFactory == null) {
Application application = null;
Context appContext = requireContext().getApplicationContext();
while (appContext instanceof ContextWrapper) {
if (appContext instanceof Application) {
application = (Application) appContext;
break;
}
appContext = ((ContextWrapper) appContext).getBaseContext();
}
if (application == null && FragmentManager.isLoggingEnabled(Log.DEBUG)) {
Log.d(FragmentManager.TAG, "Could not find Application instance from "
+ "Context " + requireContext().getApplicationContext() + ", you will "
+ "need CreationExtras to use AndroidViewModel with the default "
+ "ViewModelProvider.Factory");
}
mDefaultFactory = new SavedStateViewModelFactory(
application,
this,
getArguments());
}
return mDefaultFactory;
}

很好他们都返回了SavedStateViewModelFactory()。看下SavedStateViewModelFactorycreat();

  • SavedStateViewModelFactory.create()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
less复制代码  @NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
// ViewModelProvider calls correct create that support same modelClass with different keys
// If a developer manually calls this method, there is no "key" in picture, so factory
// simply uses classname internally as as key.
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return create(canonicalName, modelClass);
}


public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
//判断是否是isAndroidViewModel
boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
Constructor<T> constructor;
if (isAndroidViewModel) {
constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
} else {
constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
}
// doesn't need SavedStateHandle
if (constructor == null) {
return mFactory.create(modelClass);
}

SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
try {
T viewmodel;
if (isAndroidViewModel) {
viewmodel = constructor.newInstance(mApplication, controller.getHandle());
} else {
viewmodel = constructor.newInstance(controller.getHandle());
}
viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
return viewmodel;
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to access " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("An exception happened in constructor of "
+ modelClass, e.getCause());
}
}

一个参数调用俩参数,判断了是否是判断是否是AndroidViewModel,再往下一看newInstance(),反射, 绝对的反射。

到此为止,我们知道 get() 其实就是缓存则取,没缓存就反射搞个对象 ,also 缓存进去。

至此,还没成艺术。

ViewModel 的自我修养(管理)?

认真阅读的朋友们都知道,Fragment正常情况下用FragmentActivityViewModelStore

  • 看下FragmentActivity 的 父类ComponentActivity 构造参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
less复制代码public ComponentActivity() {

//省略部分代码
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {

mContextAwareHelper.clearAvailableContext();

if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});




}

我们看到 做了个监听,在 *ON_DESTROY*的时候, 判断了下!isChangingConfigurations()也就是 不是发生了配置变化,是真正销毁的时候,调用了 getViewModelStore().clear()

其实就是相当于把当前缓存的ViewModel 的对象嘎嘎的清空了。

经常说ViewModel 为什么不建议持有Context。 因为是在onDestroy后才执行。

也为什么说旋转屏幕ViewModel 不会丢数据。因为虽然走了onDestroy 但是内部判断了是否旋转屏幕。

  • Fragment 怎么管理的呢

我们记得Fragment 在 this 那一步的时候,有2种 情况,一种 拿FragmentActivity的ViewModelStore 。一种自己构建了一个FragmentManagerViewModel();

这个mNonConfig 其实在创建后 赛进了mFragmentStore

1
2
3
4
5
6
7
8
9
10
11
ini复制代码 if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
// Ensure that the state is in sync with FragmentManager
mNonConfig.setIsStateSaved(isStateSaved());
mFragmentStore.setNonConfig(mNonConfig);

mFragmentStore.setNonConfig(mNonConfig);mNonConfig传出去了,追着看一下。

进入 FragmentStore

发现 FragmentStore 是在FragmentManager初始化就创建了

1
java复制代码 private final FragmentStore mFragmentStore = new FragmentStore();

FragmentStore里调用mNonConfig 查看下被调用的地方。

1
2
3
csharp复制代码 FragmentManagerViewModel getNonConfig() {
return mNonConfig;
}

进入FragmentManager.java

->clearBackStackStateViewModels()

这里去判断了哪些Fragment 需要清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ini复制代码private void clearBackStackStateViewModels() {
boolean shouldClear;
if (mHost instanceof ViewModelStoreOwner) {
shouldClear = mFragmentStore.getNonConfig().isCleared();
} else if (mHost.getContext() instanceof Activity) {
Activity activity = (Activity) mHost.getContext();
shouldClear = !activity.isChangingConfigurations();
} else {
shouldClear = true;
}
if (shouldClear) {
for (BackStackState backStackState : mBackStackStates.values()) {
for (String who : backStackState.mFragments) {
mFragmentStore.getNonConfig().clearNonConfigState(who, false);
}
}
}
}

->dispatchDestroy()

1
2
3
4
5
6
7
scss复制代码 void dispatchDestroy() {
mDestroyed = true;
execPendingActions(true);
endAnimatingAwayFragments();
clearBackStackStateViewModels();
//省略代码
}

-> FragmentController.java->dispatchDestroy()

1
2
3
csharp复制代码public void dispatchDestroyView() {
mHost.mFragmentManager.dispatchDestroyView();
}

-> FragmentActivity.java->onDestroy(){}

1
2
3
4
5
6
scss复制代码 @Override
protected void onDestroy() {
super.onDestroy();
mFragments.dispatchDestroy();
mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
}

把整个调用反过来看一下,FragmentActivityonDestroy 调用

mFragments.dispatchDestroy(); (FragmentController.java)

-> FragmentManager.dispatchDestroy() (FragmentManger.java)

-> clearBackStackStateViewModels()

可以理解为FragmentActivity销毁才销毁。

另外 FragmentStateManager 里 重新构建的时候,也会销毁清理对应的ViewModel。感兴趣可以看一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
scss复制代码void moveToExpectedState{
//省列部分代码
case Fragment.ATTACHED:
if (mFragment.mBeingSaved
&& mFragmentStore.getSavedState(mFragment.mWho) == null) { mFragmentStore.setSavedState(mFragment.mWho, saveState());
}
destroy();
}
void destroy() {
//省列部分代码
if (shouldDestroy) {
mFragment.performDestroy();
if ((beingRemoved && !mFragment.mBeingSaved) || shouldClear) { mFragmentStore.getNonConfig().clearNonConfigState(mFragment, false);
}
mFragment.performDestroy();
}
}

面试的侃侃而谈

  • 优势
+ 保存数据,页面变化能缓存
+ 自动管理,页面销毁自动清理
+ `Fragment`和 `Activity`可共用。
  • Fragment能拿 ActivityViewModel么?
+ 能 ,毕竟`FragmentManger` 那向上管理,其实取的就是上层`FragmentActivity`的`ViewModelStore`。
  • 怎么自动管理的?
+ `ComponentActivity` 监听`onDestroy()` ,清理
+ `Fragment`在`FragmentActivity` 的 `onDestroy()` 会清理。
  • ViewModelStore 知道么?
+ 知道 ,一个`map` 就是干。
  • 知道怎么创建的么?
+ 内部 `factory` 反射就是干。
  • 为啥旋转还能保存数据?

判断了是配置变化,如旋转屏幕等,等到真正销毁才清空。

  • 剩下的自行发挥

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%