为什么要对 ViewModel 进行理解。
一切都是因为一个bug 而起。
开发模式( 一个Activity + 多个fragment 组合的回退栈模式 )
过程描述:
第一步: 当用户点击 新闻列表
第二步: 然后点击详情 进入详情界面
第三步: 看见在详情界面有不喜欢一栏。
第四步: 点击不喜欢 回到上一个界面 列表数据会自动 删除掉。
看样子 合理来说是正常的对吧。可以删除。
错。
还有一种情况。
在我们按 home 键的时候, 系统内存不足 把 app 杀掉。或者 我们 把 开发者模式打开 (进入下一个页面 不保留当前activity )进入下一个页面
然后 按照过程描述 操作 就会出现崩溃。
因为这个时候 item == null 。。
我当时查看情况是这样的
app 被系统重建
savedInstanceState 有值。
然后我就想起来
NewListVM viewModel = ViewModelProviders.of(this(fragment)).get(NewListVM.class);
不是应该可以在这种情况下 可以获取之前保存的 ViewModel 吗?
怀着这样的想法 我就试了试。(可以当之前我是直接New NewListVM() 设置进去的。 )
然后发现还是报错。 然后我就生气了,这tm 算什么 可以重用之前的ViewModel ????
debug 大法。
ViewModelProviders.class
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) fragment).getViewModelStore();
}
return holderFragmentFor(fragment).getViewModelStore();
}
这里明明写的。 为什么就不行。
当时我想到了 是不是 没有处理 fragment 回收场景。
代码是这样的。
public Fragment initFragment(Class aClass, String title) {
Fragment fragment = fragmentManager.findFragmentByTag(title);
if (fragment == null) {
Bundle bundle = new Bundle();
bundle.putString(TITLT_KEY, title);
fragment = Fragment.instantiate(mContext, aClass.getName(), bundle);
}
return fragment;
}
明显的有处理 内存回复的情况。
还是莫名其妙 搞不懂。
然后我就想是不是谁吧 存在 getViewModelStore() ViewModel 给它clear掉了。
然后就找到了 HolderFragment 毕竟clear 方法是写在这个里面的。
HolderFragment 主要作用:
依附于 activity 或者 fragment 之上 存储 + 感知 是否被销毁掉(onDestroy)。
存储 基于 activity 或 fragment 作用域 的值。
然后我就想起来 我在fragment的 onDestroy 打一个Log 就可以了。
但是由于我使用的时kill 就一直没有走 onDestroy 方法。
但是不走 不等于没有被清除。
(有一个重点标记 kill 掉或者被杀掉 不会都走 onDestroy 如果要在 该方法做标记就需要注意了 )
然后我就很奇怪, 然后就找到了另外一个方法 就是 查看内存地址。
既然 HolderFragment 是依附于 fragment 那么 fragment 变化(不是之前的一个) 这个 getViewModelStore() 也就不是之前的。
(重点标记二) kill 进程之后 从 getSupportFragmentManager().getFragments() 取出来的值并不是 之前的fragment 系统会默认帮你创建一个无参数的构造方法。
解决办法:
就是 配合
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if(savedInstanceState != null){
ArrayList item = savedInstanceState.getParcelableArrayList("newList");
// 逻辑赋值
}else{
// 正常情况
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("newList", viewModel.getItems());
}
意外 收获:
内存回收掉之后。
fragment.
onSaveInstanceState -> onCreateView() (savedInstanceState !=null ) -> onActivityResult
参考:
https://medium.com/androiddevelopers/viewmodels-persistence-onsaveinstancestate-restoring-ui-state-and-loaders-fc7cc4a6c090
https://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle,%20android.os.PersistableBundle)
https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate
https://developer.android.com/topic/libraries/architecture/saving-states.html
https://developer.android.com/topic/libraries/architecture/viewmodel
https://developer.android.com/guide/components/activities/?hl=zh-cn