Android Jetpack Components of Lifecycle 学习笔记
Android Jetpack Components of LiveData 学习笔记
Android Jetpack Components of ViewModel 学习笔记
Demo 地址:https://github.com/mengzhinan/Lifecycle_LiveData_ViewModel_demo
ViewModel Google 文档:https://developer.android.google.cn/topic/libraries/architecture/viewmodel
环境配置:
与 Lifecycle 和 LiveData 略有不同,在其基础上,还需要导入依赖:
// viewmodel 需要添加的配置
implementation 'android.arch.lifecycle:extensions:1.1.1'
ViewModel 是什么?
ViewModel 类的设计目的是以生命周期意识的方式存储和管理与UI相关的数据。ViewModel 类允许数据在配置更改(如屏幕旋转)中生存。
以我的理解总结为:
1、ViewModel 可以实现在同一个 Activity 对象下的多个 Fragment 之间数据共享。
2、ViewModel 可以实现在手机屏幕旋转前后数据共享,避免不必要的重复数据请求。
先从 Demo 使用看起吧。还是上一篇文章的 LiveData Demo:
public class DataUtil {
private MutableLiveData<String> name = new MutableLiveData<>();
public LiveData<String> getNameLiveData(){
return name;
}
public void requestHead() {
// 模拟请求网络或 DB,然后更新数据
name.setValue("requestHead success");
}
public void requestList() {
// 模拟请求网络或 DB,然后更新数据
name.setValue("requestList success");
}
}
public class LiveDataActivity extends AppCompatActivity {
private DataUtil dataUtil;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dataUtil = new DataUtil();
dataUtil.getNameLiveData().observe(this, data -> {
// 得到数据更新
});
}
@Override
protected void onResume() {
super.onResume();
dataUtil.requestHead();
dataUtil.requestList();
}
}
上面代码其实有个问题,如果 DataUtil 重复创建的话,将会对应的重新创建 MutableLiveData 对象,故无法实现数据共享。先别钻牛角,看 ViewModel 的用法:
public class ListViewModel extends ViewModel {
private MutableLiveData<String> headLiveData;
private MutableLiveData<String> listLiveData;
public LiveData<String> getHeadLiveData() {
if (headLiveData == null) {
headLiveData = new MutableLiveData<>();
}
return headLiveData;
}
public LiveData<String> getListLiveData() {
if (listLiveData == null) {
listLiveData = new MutableLiveData<>();
}
return listLiveData;
}
public void requestHead() {
// TODO: 2019-07-29
headLiveData.setValue("head request success");
}
public void requestList() {
// TODO: 2019-07-29
listLiveData.setValue("list request success");
}
}
public class ViewModelActivity extends AppCompatActivity {
private ListViewModel listViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listViewModel = ViewModelProviders.of(this).get(ListViewModel.class);
listViewModel.getHeadLiveData().observe(this, data->{
// 头部数据更新
});
listViewModel.getListLiveData().observe(this, data->{
// list 数据更新
});
}
@Override
protected void onResume() {
super.onResume();
listViewModel.requestHead();
listViewModel.requestList();
}
}
代码说明:
DataUtil 逻辑更改为了 ListViewModel,然后继承了 ViewModel 类。其他的没有本质变化。
在 ViewModelActivity 中,唯一的变化是 ListViewModel 的创建方式变为了:
listViewModel = ViewModelProviders.of(this).get(ListViewModel.class);
我们先来分析 ViewModel 类:
public abstract class ViewModel {
/**
* This method will be called when this ViewModel is no longer used and will be destroyed.
* <p>
* It is useful when ViewModel observes some data and you need to clear this subscription to
* prevent a leak of this ViewModel.
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
这是一个抽象类,其中只有一个方法 onCleared(),当 ViewModel 所在的 Activity 销毁时,回调此方法清理资源。
ViewModel 有一个比较常用的子类 AndroidViewModel,提供 application 对象:
/**
* Application context aware {@link ViewModel}.
* <p>
* Subclasses must have a constructor which accepts {@link Application} as the only parameter.
* <p>
*/
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}
我们再回头分析 ViewModel 获取的方法:
ViewModelProviders.of(this).get(ListViewModel.class);
先追查 get() 方法:
@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);
}
继续追查 get() 重载方法:
@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 {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
如果传递的 ViewModel 已经缓存了,则把缓存的 ViewModel 对象直接返回;否则才创建新的对象。
那么 mViewModelStore 是如何做到缓存的呢?
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
发现 mViewModelStore 是在 ViewModelProvider 的构造函数中赋值的。追查到 ViewModelProviders 的 of 方法中:
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
追索到 ViewModelStores 的 of 方法:
@NonNull
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}
找到 HolderFragment 中的 holderFragmentFor() 方法:
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}
其实看到这儿,基本可以看出原理:
在 Activity 中创建一个 HolderFragment 对象,并添加到 FragmentManager 中。在 HolderFragment 中保存 ViewModel 集合。
因此,当 Activity 销毁时,也会同步销毁 HolderFragment。
那为什么说手机屏幕旋转前后,ViewModel 不会销毁呢?
追查代码,HolderFragment 类的构造函数:
public HolderFragment() {
setRetainInstance(true);
}
setRetainInstance(true); 这行代码应该是设置避免在屏幕旋转时重建 Fragment。
所以,总结 ViewModel 的特点:
ViewModel 是依附 HolderFragment 保存在 Activity 的 FragmentManager 中的。在同一个 Activity 下的任意位置、任意 Fragment 中通过 ViewModelProviders.of(this).get(xxx) 方式获取 ViewModel,都会是同一个对象,故避免了重复请求,实现数据共享。
由于 HolderFragment 的特点,可避免屏幕旋转后的数据丢失,避免重复请求。
Demo 地址:https://github.com/mengzhinan/Lifecycle_LiveData_ViewModel_demo