为什么需要以上的功能?主要有两点1.数据的获取以显示数据 2.生命周期匹配以防止内存泄漏。
为了完成以上三点的功能,我们需要什么?
Loaders,在非主线程完成需要的数据加载工作之后将数据更新到界面,并时刻监听数据。同时在onDestroy中释放资源。
public class Loader extends AsyncTask{
T startLoading(){
T = loadData();
T.observe();
return T;
}
stopLoading(){
T.unobserve();
T = null;
}
}
这是我们最先想到的,也是传统的方式,但是这种方式,也就是LoaderManager在8.1中被弃用。我们可以通过这种方式完成很多功能,其缺点我现在感觉到的只有开发上面的问题,与Activity Fragment都耦合比较密切。 为了完成所需要的功能,需要重写许多方法。
在8.1或者更高的版本中,google官方推荐了ViewModel模块用来存储数据。独立于Activity的生命周期又可以感知其生命周期。先来看一下ViewModel是什么样子:
public abstract class ViewModel {
/**
* This method will be called when this ViewModel is no longer used and will be destroyed.
*
* 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() {
}
}
ViewModel只有一个接口,生命周期如下: 那么,ViewModel会不会在Activity被销毁时重建,或者还是会保留数据。
正如官方所说,是保留数据:
在Activity被销毁后,还是会重新恢复自己的对象。那么这是如何做到的呢? 我们从创建过程开始看起:
ViewModelProviders.of(this).get(UserProfileViewModel.class);
ViewModelProviders.of() 返回ViewModelProvider,两种情况:
return new ViewModelProvider(ViewModelStores.of(activity), factory);
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
factory相同,都使用默认的Factory类。接下来看一下ViewModelStores,对于activity和fragment又有什么处理。
public static ViewModelStore of(Fragment fragment) {
return holderFragmentFor(fragment).getViewModelStore();
}
在这里需要在ViewModelStores中获取ViewModelStore。
HolderFragment.holderFragmentFor,根据传入的activity或者fragment返回了一个Fragment的子类,HolderFragment。
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
}
HolderFragment里面重点就是一个HolderFragmentManager,在这里还维护了一个静态的manager。这个manager里面有什么?
static class HolderFragmentManager {
private Map mNotCommittedActivityHolders = new HashMap<>();
private Map mNotCommittedFragmentHolders = new HashMap<>();
private ActivityLifecycleCallbacks mActivityCallbacks =
new EmptyActivityLifecycleCallbacks() {
@Override
public void onActivityDestroyed(Activity activity) {
HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
if (fragment != null) {
Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
}
}
};
private boolean mActivityCallbacksIsAdded = false;
private FragmentLifecycleCallbacks mParentDestroyedCallback =
new FragmentLifecycleCallbacks() {
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {
super.onFragmentDestroyed(fm, parentFragment);
HolderFragment fragment = mNotCommittedFragmentHolders.remove(
parentFragment);
if (fragment != null) {
Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
}
}
};
两个map容器,两个回调,一个标志。然后看一下holderfragmentfor方法
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = mNotCommittedActivityHolders.get(activity);
if (holder != null) {
return holder;
}
if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
}
holder = createHolderFragment(fm);
mNotCommittedActivityHolders.put(activity, holder);
return holder;
}
HolderFragment holderFragmentFor(Fragment parentFragment) {
FragmentManager fm = parentFragment.getChildFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = mNotCommittedFragmentHolders.get(parentFragment);
if (holder != null) {
return holder;
}
parentFragment.getFragmentManager()
.registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
holder = createHolderFragment(fm);
mNotCommittedFragmentHolders.put(parentFragment, holder);
return holder;
}
完犊子,这个activity需要时fragmentactivity呀,也没有报错,结果也是对的,先假设这里对吧,标红一下。
这里看逻辑还得把另外两个方法也粘过来。
private static HolderFragment findHolderFragment(FragmentManager manager) {
if (manager.isDestroyed()) {
throw new IllegalStateException("Can't access ViewModels from onDestroy");
}
Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
throw new IllegalStateException("Unexpected "
+ "fragment instance was returned by HOLDER_TAG");
}
return (HolderFragment) fragmentByTag;
}
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment();
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
先给个manager看看有没有用指定的这个tag创建的holderfragment。当然没有,第一次。然后继续看看map里边有没有,很显然,也没有。。。。继续。。先不管这个callback,我们去创建一个holderfragment,new一个实例,通过标签和manager加到栈里面去,然后也放到map里边。
这个holderfragment我理解就是提供这个viewModelStore,为了让别的组建都有这个东东,好方便去存咱们的viewModel 现在有了provider了,然后就factory实例化出来吧。这个store实际上是在一个已经加到栈中的holderfragment存储着,就经验来说这个是不会随activity销毁的,讲真。再向下就太多了,实验一下就结束。
我们在用fragment的时候需要用transcation去动态的控制,add replace remove 所以这个是独立于activity的,viewmodel也就存储下来了。
看了这么多,也就是为了知道为什么viewModel可以把数据存下来。而且现在也只是大概知道,还不可避免有一些理解上的误差。
继续。
接下来就是通知数据的变更,只需要在我们的数据外套一层,LiveData
viewModel.getUser().observe(this, user -> {
// update UI
});
这里有个重点,LiveData是可以自己处理生命周期变化的,具体就是lifeCycle这套机制和support中的activity结合起来实现的,虽然有一些小问题,但是不会妨碍我们使用,我暂时不想知道他们是如何去处理这些生命周期的问题的,我想知道,什么样子的数据变化会引起这个回调的调用。
要知道的是,我们的数据在viewModel中虽然可以保持很长时间,但依旧是暂时的,我们需要从数据库中查找,并且可能会在网络上更新,这些如何处理? google提供的这个结构中提供了一个Room的数据库封装,但是这还不是我想关注的重点。我想我需要先梳理一下我的关注点:
1. 我需要界面显示数据,这个数据来自数据库。
2.我需要可以交互触发网络更新,更新的数据存入数据库,并通知界面。
3.如果更新失败,界面也需要知道失败的信息。
4.后台会有一个jobservice在定期的更新,我不能让前台的更新和这个服务冲突。
如果我前台更新是调用jobservice一样的服务,那么我只能通过contentobserver去监听数据库或者服务发送本地的广播给我,如果更新失败,只能是使用广播,所以,更新的接口都是相同的,并且解决掉冲突不要同时调用,前台的时候就使用viewModel这一套。
LiveData是可以setData的,一设置数据肯定就变了呀,就直接回调了,这是个不错的方式。
单一职责,我们获取数据需要封装另外一个类。
接下来的任务就只需要熟悉这些借口,做一些实验然后和自己的需求挂钩。
实际上,我不想使用ViewModel,我只是想模仿这套机制,无所谓了,可以尝试一下。