ViewModel 是google推出的架构组件之一,它被设计用于存储和管理UI相关的数据。
背景:
1方便数据存储
以生命周期的方式存储和管理UI相关数据。当屏幕旋转等改变时,数据能够被恢复。
2生命周期控制
使用fragment时我们都会对它复杂的生命周期处理感到痛苦,稍有不慎报各种null异常。ViewModel,Lifecycle等组件的推出,可以有效的解决生命周期的处理问题,提高app的稳定性。`
3 DataBinding数据驱动UI
从字面意思看ViewModel就是activity fragment的数据抽象模型。我们知道activity fragment是由系统管理,不受app控制,当内存不足时,系统随时都有可能回收杀死app。所以这也是android app开发具有挑战的地方,我们需要在不确定的环境下保证我们的业务流程顺利进行。由于ViewModel设计是和activity fragment的生命周期是解耦的,所以当activity fragmegnt重新create时,如果ViewModel已经创建过,则仍使用原ViewModel。
ViewMode生命周期见下图:
如何使用:
1自定义ViewModel
class UserModel extends ViewModel{
String name;
String age;
}
2 获取ViewModel
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
UserModel model = ViewModelProviders.of(this).get(UserModel.class);
}
}
因为ViewModel生命周期感知的,所以我们不需要手动释放,使用起来非常方便。
注意事项:
ViewModel不可以持有activity fragment等view的引用,否则会导致内存泄漏。
读到这里你可能会有如下疑问:
ViewModel 为什么不可以持有activity fragment等View的引用
ViewModel 如果感知activity fragment的生命周期
ViewModel 如何保存数据
接下来我们根据上述疑问,跟踪ViewModel 的相关代码具体分析。
以ViewModelProviders.of(this).get(UserModel.class); 这行代码作为切入点
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
FragmentActivity activity = fragment.getActivity();
if (activity == null) {
throw new IllegalArgumentException(
"Can't create ViewModelProvider for detached fragment");
}
initializeFactoryIfNeeded(activity.getApplication());
return new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory);
}
1 创建默认的工厂方法,实例化新的ViewModels
2 通过传入fragment或者acitity参数,new ViewModelProvider实例并返回。
这里有2个关键的代码点
1 ViewModelStores.of(fragment)
2 return new ViewModelProvider(…)
针对代码点1 ViewModelStores.of(fragment) 查看ViewModelStores源码
@MainThread
public static ViewModelStore of(FragmentActivity activity) {
return holderFragmentFor(activity).getViewModelStore();
}
它通过holderFragmentFor函数返回ViewModelStore对象
继续跟踪holderFragmentFor函数,找到HolerFragment 这个关键类
从HolerFragment类的代码中我们找到了ViewModel感知fragment,activity生命周期的原因。
Google在底层默默的create了一个新的HolderFragment 对象,
由HolderFragment负责生命周期感知,当onDestroy()时清理ViewModelStore
持有ViewModelStore对象,setRetainInstance(true) 保证当界面旋转被销毁再重建时保证mViewModelStore 不被销毁。但对于因内存被系统杀死后重新进入,数据不会被恢复。
public class HolderFragment extends Fragment {
private ViewModelStore mViewModelStore = new ViewModelStore();
public HolderFragment() {
setRetainInstance(true);
}
HolderFragment holderFragmentFor(Fragment parentFragment) {
FragmentManager fm = parentFragment.getChildFragmentManager();
HolderFragment holder = findHolderFragment(fm);
......
holder = createHolderFragment(fm);
mNotCommittedFragmentHolders.put(parentFragment, holder);
return holder;
}
}
解决了ViewModel如果感知生命周期的问题,我们再分析下ViewModel的数据存储。
我们查看ViewModelStore这个类,发现ViewModel是被存储在一个HashMap内,所以它是保存在app内存中,并没有做持久化处理。
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
......
}
针对代码点2 return new ViewModelProvider(…),我们看下ViewModelProvider。
从代码中可知ViewModelProvider是对ViewModelStore 和Factory的封装,当viewModel存着时获取ViewModel,如果不存在则调用工厂方法创建ViewModel。
public class ViewModelProvider {
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
public ViewModelProvider(ViewModelStore store, Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
......
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
}
总结:
通过代码的跟踪分析我们解决了3大疑问,发现ViewModel的实现主要依赖于HolderFragment类的实现。
通过添加新的HolderFragment感知生命周期的变化
通过HolderFragment持有ViewModelStore对象
ViewModel存储在ViewModelStore的hashmap内存中,不做持久化数据存储,当activity fragment处于后台因内存问题被系统杀死后,重新进入后数据不会被恢复。
Fragment和Activity作为key访问获取ViewModel对象
ViewModel不能持有activity,fragment等view的引用,避免内存泄漏
参考
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html
android source code 8.0