对于当前的界面,可能由于配置、旋转或是其他的原因导致当前的界面异常销毁,当出现这种情况时,界面恢复时也同时需要恢复一些数据,之前的做法就是利用onSaveInstanceState()在异常销毁时将数据保存起来,然后在页面重新启动时通过onRestoreInstanceState()对数据进行恢复,这种做法通常是对于一些简单的数据进行保存。现假如当前界面有个网络请求,当界面异常销毁后重新创建时不在进行网络请求,而是直接使用上一次请求的数据,这时该怎么去做呢?这时就需要使用到这里的主角ViewModel了。
现在来看看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() {
}
}
还是很简单的,里面就是定义了一个方法,这个方法会在onDestroy()中被调用,所以这个方法里可以做一些释放资源的工作,他有一个实现类AndroidViewModel,这个类主要是提供了一个全局的Application,这样就可以在这个类中使用全局上下文了。
首先是定义一个继承ViewModel(如果需要Application,可以继承AndroidViewModel)类,如下:
public class CustomViewModel extends ViewModel {
public MutableLiveData> listData = new MutableLiveData<>();
public void getListData(){
//假设这里是一个网络请求
new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(3);
ArrayList list = new ArrayList();
for (int i = 0; i < 10;i++) {
list.add("string data index = "+i);
}
listData.postValue(list);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
这里需要你知道对LiveData的使用,这里使用延时模拟定义了一个网络请求,在三秒后将数据返回,这里定义完ViewModel后并不能实现一开始所说的对数据的保存,那还要怎么做呢?接下来就是重点了,为了达到一开始所说的效果,这里就需要说到Google提供的一个扩展库了,引入:
implementation 'android.arch.lifecycle:extensions:1.1.1'
用法如下:
CustomViewModel model = ViewModelProviders.of(this).get(HomeModel.class)
这个扩展库的作用就是为我们创建ViewModel对象并维护,用法很简单,接下来的重点就是去分析它是如何帮我们维护这个ViewModel对象的。
这里的切入自然是ViewModelProviders这个类了,这个一个工厂类,这里要看的就是他的of()方法,这里需要传入的对象是FragmentActivity/Fragment,先来看下这个类的定义:
public class ViewModelProviders {
/**
* @deprecated This class should not be directly instantiated
*/
@Deprecated
public ViewModelProviders() {
}
private static Application checkApplication(Activity activity) {
Application application = activity.getApplication();
if (application == null) {
throw new IllegalStateException("Your activity/fragment is not yet attached to "
+ "Application. You can't request ViewModel before onCreate call.");
}
return application;
}
private static Activity checkActivity(Fragment fragment) {
Activity activity = fragment.getActivity();
if (activity == null) {
throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
}
return activity;
}
/**
* Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given
* {@code fragment} is alive. More detailed explanation is in {@link ViewModel}.
*
* It uses {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels.
*
* @param fragment a fragment, in whose scope ViewModels should be retained
* @return a ViewModelProvider instance
*/
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
return of(fragment, null);
}
/**
* Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity
* is alive. More detailed explanation is in {@link ViewModel}.
*
* It uses {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels.
*
* @param activity an activity, in whose scope ViewModels should be retained
* @return a ViewModelProvider instance
*/
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
/**
* Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given
* {@code fragment} is alive. More detailed explanation is in {@link ViewModel}.
*
* It uses the given {@link Factory} to instantiate new ViewModels.
*
* @param fragment a fragment, in whose scope ViewModels should be retained
* @param factory a {@code Factory} to instantiate new ViewModels
* @return a ViewModelProvider instance
*/
@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);
}
/**
* Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity
* is alive. More detailed explanation is in {@link ViewModel}.
*
* It uses the given {@link Factory} to instantiate new ViewModels.
*
* @param activity an activity, in whose scope ViewModels should be retained
* @param factory a {@code Factory} to instantiate new ViewModels
* @return a ViewModelProvider instance
*/
@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);
}
/**
* {@link Factory} which may create {@link AndroidViewModel} and
* {@link ViewModel}, which have an empty constructor.
*
* @deprecated Use {@link ViewModelProvider.AndroidViewModelFactory}
*/
@SuppressWarnings("WeakerAccess")
@Deprecated
public static class DefaultFactory extends ViewModelProvider.AndroidViewModelFactory {
/**
* Creates a {@code AndroidViewModelFactory}
*
* @param application an application to pass in {@link AndroidViewModel}
* @deprecated Use {@link ViewModelProvider.AndroidViewModelFactory} or
* {@link ViewModelProvider.AndroidViewModelFactory#getInstance(Application)}.
*/
@Deprecated
public DefaultFactory(@NonNull Application application) {
super(application);
}
}
}
代码比较简单,可以分为三步理解:
接下来就来分析第二点和第三点,先来看ViewModelProvider.AndroidViewModelFactory这个类:
/**
* {@link Factory} which may create {@link AndroidViewModel} and
* {@link ViewModel}, which have an empty constructor.
*/
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
/**
* Retrieve a singleton instance of AndroidViewModelFactory.
*
* @param application an application to pass in {@link AndroidViewModel}
* @return A valid {@link AndroidViewModelFactory}
*/
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
/**
* Creates a {@code AndroidViewModelFactory}
*
* @param application an application to pass in {@link AndroidViewModel}
*/
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
public T create(@NonNull Class modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
}
/**
* Simple factory, which calls empty constructor on the give class.
*/
public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public T create(@NonNull Class modelClass) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
是一个单例对象,这里主要是看它的create()方法,可以看到这里主要是利用反射构建了ViewModel对象。接着就要看看ViewModelProvider 的get()方法是如何返回ViewModel对象的:
public class ViewModelProvider {
private static final String DEFAULT_KEY =
"android.arch.lifecycle.ViewModelProvider.DefaultKey";
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
*
*
* @param modelClass a {@code Class} whose instance is requested
* @param The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
T create(@NonNull Class modelClass);
}
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
*
* @param owner a {@code ViewModelStoreOwner} whose {@link ViewModelStore} will be used to
* retain {@code ViewModels}
* @param factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in the given {@code store}.
*
* @param store {@code ViewModelStore} where ViewModels will be stored.
* @param factory factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
/**
* Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
* an activity), associated with this {@code ViewModelProvider}.
*
* The created ViewModel is associated with the given scope and will be retained
* as long as the scope is alive (e.g. if it is an activity, until it is
* finished or process is killed).
*
* @param modelClass The class of the ViewModel to create an instance of it if it is not
* present.
* @param The type parameter for the ViewModel.
* @return A ViewModel that is an instance of the given type {@code T}.
*/
@NonNull
@MainThread
public T get(@NonNull Class 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);
}
/**
* Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
* an activity), associated with this {@code ViewModelProvider}.
*
* The created ViewModel is associated with the given scope and will be retained
* as long as the scope is alive (e.g. if it is an activity, until it is
* finished or process is killed).
*
* @param key The key to use to identify the ViewModel.
* @param modelClass The class of the ViewModel to create an instance of it if it is not
* present.
* @param The type parameter for the ViewModel.
* @return A ViewModel that is an instance of the given type {@code T}.
*/
@NonNull
@MainThread
public T get(@NonNull String key, @NonNull Class 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;
}
}
重点看下这里的get()方法,可以看到这里获取ViewModel的一个流程:
接着来看通过构造方法传进来的mViewModelStore对象,这个对象是通过ViewModelStores.of(activity)返回的,来看下这个类:
public class ViewModelStores {
private ViewModelStores() {
}
/**
* Returns the {@link ViewModelStore} of the given activity.
*
* @param activity an activity whose {@code ViewModelStore} is requested
* @return a {@code ViewModelStore}
*/
@NonNull
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}
/**
* Returns the {@link ViewModelStore} of the given fragment.
*
* @param fragment a fragment whose {@code ViewModelStore} is requested
* @return a {@code ViewModelStore}
*/
@NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) fragment).getViewModelStore();
}
return holderFragmentFor(fragment).getViewModelStore();
}
}
这里会调用holderFragmentFor(activity).getViewModelStore()进行返回,holderFragmentFor()是HolderFragment类中的方法,而HolderFragment继承自Fragment,这里就是实现Activity异常销毁重新创建时恢复数据的关键,现在就来重点关注下这个类的实现,整个过程将在代码中进行讲解:
public class HolderFragment extends Fragment implements ViewModelStoreOwner {
private static final String LOG_TAG = "ViewModelStores";
private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final String HOLDER_TAG =
"android.arch.lifecycle.state.StateProviderHolderFragment";
//这里保存的就是ViewModel对象
private ViewModelStore mViewModelStore = new ViewModelStore();
public HolderFragment() {
setRetainInstance(true);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sHolderFragmentManager.holderFragmentCreated(this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
//使用了代理的方式,那就去看看sHolderFragmentManager
return sHolderFragmentManager.holderFragmentFor(activity);
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
}
@SuppressWarnings("WeakerAccess")
static class HolderFragmentManager {
//维护了两个集合,根据不同的情况来进行保存HolderFragment
private Map mNotCommittedActivityHolders = new HashMap<>();
private Map mNotCommittedFragmentHolders = new HashMap<>();
private ActivityLifecycleCallbacks mActivityCallbacks =
new EmptyActivityLifecycleCallbacks() {
@Override
public void onActivityDestroyed(Activity 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);
//fragment销毁时需要及时清理掉保存的HolderFragment,以免内存泄露
HolderFragment fragment = mNotCommittedFragmentHolders.remove(
parentFragment);
if (fragment != null) {
Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
}
}
};
//当调用fragment的onCreate()时会调用到这里,移除可能还保存的HolderFragment对象
void holderFragmentCreated(Fragment holderFragment) {
Fragment parentFragment = holderFragment.getParentFragment();
if (parentFragment != null) {
mNotCommittedFragmentHolders.remove(parentFragment);
parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
mParentDestroyedCallback);
} else {
mNotCommittedActivityHolders.remove(holderFragment.getActivity());
}
}
//根据FragmentManager中是否保存了HolderFragment进行返回
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;
}
//创建一个HolderFragment对象并添加到FragmentManager中
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment();
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
//这里就是去获取HolderFragment对象,
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;
}
}
}
代码量不多,如果对fragment比较了解的话也比较好了解,这里有个fragment的方法setRetainInstance()需要知道,当这里传入的是true时,那么当Activity异常销毁时,fragment会进行保存,当下次activity重新创建时,会直接使用这个fragment而不会重新创建,HolderFragment中有个变量mViewModelStore,它里面存放的ViewModel,所以,当activity重新创建并获取ViewModel时,并不会在次去创建ViewModel对象,这样就可以直接使用ViewModel中存放的数据了。
就是利用了fragment的setRetainInstance(true)特性。
第一次通过ViewModelProviders创建ViewModel时,会先创建一个HolderFragment,并会将它添加FragmentManager中去,它里面还有一个mViewModelStore变量,主要作用就是用来存放ViewModel对象的,之后再通过反射创建一个ViewModel对象,并将这个对象添加到HolderFragment的mViewModelStore(内部维护的是一个HashMap)中,可以这样理解,只要这个HolderFragment存在,那么它维护的ViewHolder就不会重新创建,所以Activity异常销毁后重新创建只要保证HolderFragment不变就可以,这时setRetainInstance(true)就起到了这个作用。利用这个特性,可以使用ViewModel在activity和fragment之间进行数据传输。