- Android Architecture Component之Lifecycle-Aware Components源码分析
- Android Architecture Component之LiveData源码分析
/**
* Control whether a fragment instance is retained across Activity
* re-creation (such as from a configuration change). This can only
* be used with fragments not in the back stack. If set, the fragment
* lifecycle will be slightly different when an activity is recreated:
*
* - {@link #onDestroy()} will not be called (but {@link #onDetach()} still
* will be, because the fragment is being detached from its current activity).
* - {@link #onCreate(Bundle)} will not be called since the fragment
* is not being re-created.
* - {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} will
* still be called.
*
*/
public void setRetainInstance(boolean retain) {
mRetainInstance = retain;
}
ViewModel类旨在以一种有生命周期的方式存储和管理与UI相关的数据。 ViewModel类允许数据在屏幕旋转等配置变化后存活
解决的问题
Android框架控制着UI控制器(Activity或者Fragment)的生命周期,它就有可能决定销毁或者重建我们的UI控制,以响应完全不受控制的某些用户操作或设备事件。那么就会出现几个问题。
如果被销毁那么存储在其中的所有瞬态UI相关数据都将丢失.
例如:您的应用可能会在其中一个活动中包含用户列表。 当针对配置更改重新创建活动时,新活动必须重新获取用户列表。 对于简单的数据,活动可以使用onSaveInstanceState()方法并从onCreate()的包中恢复其数据,但是这种方法仅适用于可以序列化然后反序列化的少量数据,而不适用于潜在的大量数据像用户或位图的列表。
UI控制获取数据的时候经常进行异步调用,可能需要一些时间来返回。 UI控制器需要管理这些调用,并确保系统在销毁后清理它们以避免潜在的内存泄漏。
这种管理需要大量的维护,并且在为配置更改而重新创建对象的情况下,由于对象可能不得不重新发出已经做出的调用(可能会重新请求数据),所以浪费资源.
UI控制器(如活动和片段)主要用于显示UI数据,对用户操作做出反应,或处理操作系统通信(如权限请求)。
如果要求UI控制器也负责从数据库或网络加载数据,从而增加了该类的膨胀。 为UI控制器分配过多的责任可能会导致一个类尝试单独处理应用程序的所有工作,而不是将工作委托给其他类。 通过这种方式给UI控制器分配过多的责任也使测试变得更加困难。
compile "android.arch.lifecycle:runtime:1.0.3"
compile "android.arch.lifecycle:extensions:1.0.0-rc1"
annotationProcessor "android.arch.lifecycle:compiler:1.0.0-rc1"
api用法
创建ViewModel
public class MyViewModel extends ViewModel {
private MutableLiveData> users;
public LiveData> getUsers() {
if (users == null) {
users = new MutableLiveData>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asyncronous operation to fetch users.
}
}
ViewModel对象在配置更改期间自动保留,以便它们保存的数据立即可用于下一个activity或fragment
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.
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
如果Activity重新绘制,但是ViewModel实例并不会被销毁,而是重新从ViewModel中获取数据,等到Activity销毁(退出或者被系统杀死),框架会调用onCleared()方法,以便清理资源;
警告:viewModel绝不能引用视图, Lifecycle或任何可能持有对活动上下文的引用的类。
ViewModel可以包含lifeCycleObservers.例如liveData。但是ViewModel绝不能观察生命周期感知的可观察对象(LiveData对象的更改),如果ViewModel需要上下文引用,请用AndroidModelView,它含有Application的对象.
从图中可以看出我们发现,当activity因屏幕旋转而销毁,但是ViewModel一直存在,也就是这个对象一直都在(框架核心,怎么实现源码分析会讲到),直到finished才调用clear清除内存。
一个Activity中两个Fragment进行通信是很常见的,想象一下,主 - 从Fragmetn的一种常见情况,其中有一个片段,用户从列表中选择一个项目,另一个片段显示所选项目的内容。这种情况从来都不是微不足道的,因为这两个片段都需要定义一些接口描述,而所有者活动必须将两者联系在一起。 此外,这两个片段必须处理其他片段尚未创建或可见的场景。
对于这个常见的痛点,可以用ViewModel来解决,这些Fragment可以使用Activity的Scope来共享一个ViewModel,来处理通信。
public class SharedViewModel extends ViewModel {
private final MutableLiveData- selected = new MutableLiveData
- ();
public void select(Item item) {
selected.setValue(item);
}
public LiveData
- getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
注意:我们在获取ViewModelProvider时,这两个是使用的是getActivity(),而不是当前的Fragment(后面会分析源码)。所以两个Fragment用的是同一个对象。
好处如下:
我们先找到程序的入口
ViewModelProviders.of(this).get(MyViewModel.class)
return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory)
ViewModelStores.of(activity)
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
return holderFragmentFor(activity).getViewModelStore();
}
holderFragmentFor
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}
在(iii)处调用了sHolderFragmentManager.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;
}
holder = createHolderFragment(fm);
mNotCommittedActivityHolders.put(activity, holder);
return holder;
}
首先在在Activity的supportFragmentManager中的查找,有的话就会返回
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 Map mNotCommittedActivityHolders = new HashMap<>();
没有的话就创建并放入我们的mNotCommittedActivityHolders。
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment();
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
创建Fragment对象,这是一个无ui的Fragment,==主要作用就是用ViewModelStore储存我们的ViewModel,并在Fragement的OnDestory调用ViewModelStore的clear释放所有内存==。
有人要说,fragment对象还是要重建啊,那么ViewModel也要重建啊,因为它属于Fragment,这就用到了文章开篇讲到了setRetainInstance方法。看下源码
public HolderFragment() {
setRetainInstance(true);
}
在我们初始化的时候会调用setRetainInstance,这就解决这个问题了。
我们拿到ViweModelStore回到(1)处,我们就去拿我们的ViewModel。看源码
@NonNull
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);
}
看get(DEFAULT_KEY + “:” + canonicalName, modelClass)
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;
}
首先是我们从mViewModelStore取出我们想要的ViewModel.
有的话就返回
总结:
核心思想就是HolderFragment调用setsetRetainInstance(true),使得HolderFragment在FragmentMannager调用FindFragmentBytag,找到的是同一个HolderFragment对象(无论Activity是否重绘),这也保证了HolderFragment中的ViewModelStore(存放我们ViewModel的地方)不被销毁,然后我们取出我们所要的ViewModel,进行数据读取。
官方文档