Overview
ViewModel, literally, it must be related to the view (View) and the data (Model). Just like its literal meaning, it is responsible for preparing and managing data classes related to UI components (Fragment/Activity), which means that ViewModel is used to manage For UI-related data, ViewModel can also be used for communication between UI components.
Previous problems
ViewModel is used to store and manage UI-related data. It can abstract out the data logic related to an Activity or Fragment component, and can adapt to the life cycle of the component. For example, when the screen rotates, the Activity is rebuilt. The data is still valid.
Before the introduction of ViewModel, there were several problems as follows:
Usually the Android system manages the life cycle of UI controllers (such as Activity and Fragment). The system responds to user interaction or rebuilds components, and the user cannot control it. When the component is destroyed and rebuilt, the data related to the original component will also be lost. If the data type is relatively simple and the amount of data is not large, you can store the data through onSaveInstanceState(), and read the Bundle from it through onCreate() after the component is rebuilt. Data recovery. However, if it is a large amount of data that is inconvenient to serialize and deserialize, the above method will not be applicable.
UI controllers often send a lot of asynchronous requests. It is possible that UI components have been destroyed but the request has not yet returned. Therefore, UI controllers need to do extra work to prevent memory leaks.
When the Activity is destroyed and rebuilt due to configuration changes, the general data will be re-requested. In fact, this is a waste. It is best to keep the last data.
UI controllers actually only need to be responsible for displaying UI data, responding to user interactions and system interactions. But often developers will write a lot of data request and processing work in Activity or Fragment, causing UI controllers code bloat, and also making unit testing difficult. We should follow the principle of separation of duties and separate data-related things from UI controllers.
Basic use of ViewModel
public class MyViewModel extends ViewModel {
private MutableLiveData> users;
public LiveData> getUsers() {
if (users == null) {
users = new MutableLiveData>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Asynchronous call to get user list
}
}
The new Activity is as follows:
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel. class);
model.getUsers().observe(this, users -> {
// Update UI
});
}
}
If the Activity is recreated, it will receive the same MyViewModel instance created by the previous Activity. When the belonging Activity terminates, the framework calls the onCleared() method of the ViewModel to clear the resources.
Because the ViewModel lives outside the specified Activity or Fragment instance, it should never refer to a View or hold any class that contains an Activity context reference. If the ViewModel needs the context of the Application (such as obtaining system services), you can extend the AndroidViewmodel and have a constructor to receive the Application.
Share data between Fragments
It is very common for multiple Fragments in an Activity to communicate with each other. Before each Fragment needs to define an interface description, the activity belongs to bind the two together. In addition, each Fragment must deal with situations where other Fragments are not created or are not visible. This pain point can be solved by using ViewModel. These Fragments can use their Activity to share ViewModel to handle communication:
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 onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends LifecycleFragment {
public void onActivityCreated() {
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, {item ->
// update UI
});
}
}
Note: Both of the above two Fragments use the following code to get the ViewModel, getActivity() returns the same host Activity, so the same SharedViewModel object is returned between the two Fragments.
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
1
The benefits of this approach include:
Activity does not need to do anything, nor does it need to know about communication
Fragment does not need to know each other, except SharedViewModel to communicate. If one of them (Fragment) disappears, the rest will still work as usual.
Each Fragment has its own life cycle and will not be affected by the life cycle of other Fragments. In fact, if one Fragment replaces another Fragment, the work of the UI will not be affected in any way.
The life cycle of ViewModel
The scope of the ViewModel object is determined by the Lifecycle passed to the ViewModelProvider when the ViewModel is obtained. The ViewModel stays in memory until the Lifecycle leaves permanently—for Activity, when it is finished, and for Fragment, when it is detached.
Write the picture description here
The left side of the above figure is the life cycle process of Activity, during which there is an operation to rotate the screen; the right side is the life cycle process of ViewModel.
Generally, the ViewModel is initialized by the following code:
viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class );
1
This parameter is generally Activity or Fragment, so ViewModelProvider can get the life cycle of the component.
Activity may trigger onCreate() multiple times in its life cycle, while ViewModel will only be created at the first onCreate(), and then until the last Activity is destroyed.
ViewModel related class diagram
Borrowing Android architecture components (three)-ViewModel class diagram:
Write the picture description here
ViewModelProviders is a ViewModel tool class, which provides methods to get ViewModel through Fragment and Activity, and the specific implementation is implemented by ViewModelProvider.
ViewModelProvider is a tool class that realizes the creation and acquisition of ViewModel. An interface class for creating ViewModel-Factory is defined in ViewModelProvider. There is a ViewModelStore object in ViewModelProvider, which is used to store ViewModel objects.
ViewModelStore is a class that stores ViewModels. The specific implementation is to save ViewModle objects through HashMap.
ViewModel is an abstract class, which defines only one onCleared() method, which is called when the ViewModel is not in use. ViewModel has a subclass AndroidViewModel, this class is convenient to use Context object in ViewModel, because we mentioned earlier that we cannot hold Activity reference in ViewModel.
ViewModelStores is the factory method class of ViewModelStore, it will be associated with HolderFragment, HolderFragment has a nested class-HolderFragmentManager.
ViewModel related timing diagram
Retrospecting to create a ViewModel source code, you will realize that there are a lot of steps required. Let's take the ViewModel object obtained in Fragment as an example to see the sequence diagram of the whole process.
Borrowing Android architecture components (three)-the sequence diagram of ViewModel:
Write the picture description here
The sequence diagram looks more complicated, but it only describes two processes:
Get the ViewModel object.
When the HolderFragment is destroyed, the ViewModel receives the onCleared() notification.
ViewModel related source code analysis
The concrete realization of ViewModelProviders class:
public class 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;
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(checkActivity(fragment)));
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(activity));
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
checkApplication(checkActivity(fragment));
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@NonNull Factory factory) {
checkApplication(activity);
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
ViewModelProviders provides four of() methods, which have similar functions. Of(FragmentActivity activity, Factory factory) and of(Fragment fragment, Factory factory) provide methods to customize ViewModel creation.
1. Determine whether the Fragment is Attached to Activity and whether the Application object of the Activity is empty.
2. Creating a ViewModel object seems very simple, with one line of code.
new ViewModelProvider(ViewModelStores.of(fragment), factory)
Look at the ViewModelStores.of() method first:
@NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) fragment).getViewModelStore();
}
return holderFragmentFor(fragment).getViewModelStore();
}
Continue to find out that it actually implements an interface:
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
holderFragmentFor() is a static method of HolderFragment, HolderFragment inherits from Fragment. Let's first look at the specific implementation of the holderFragment() method
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
Continue to see the specific implementation of the HolderFragmentManager.holderFragmentFor() method
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;
}
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);
}
}
};
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment(); // Create a HolderFragment object
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
public HolderFragment() {
//This is the key, which makes when the Activity is recreated, Fragment's onDestroy() and onCreate() will not be called
setRetainInstance(true);
}
setRetainInstance(boolean) is a method in Fragment. Setting this method to true can make the current Fragment survive when Activity is rebuilt, if not set or set to false, The current Fragment will also be rebuilt when the Activity is rebuilt, so that it is replaced by the newly created object.
Put a special use in the Fragment where setRetainInstance(boolean) is true For the Map that stores the ViewModel, all ViewModels in the Map will naturally survive the activity reconstruction. Let the Activity and Fragment bind to one such Fragment, store the ViewModel in the Map of the Fragment, and the ViewModel component is implemented in this way.
So far, we have got the ViewStore object. The new ViewModelProvider (ViewModelStores.of(fragment), sDefaultFactory) we are creating ViewModelProvider object is implemented by this line of code. Now let’s look at the construction method of ViewModelProvider.
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
Now you can get the ViewModel object through the ViewModelProvider.get() method, continue to see the specific implementation of this method
@NonNull
@MainThread
public extends ViewModel> T get(@ NonNull String key, @NonNull Class modelClass) {
ViewModel viewModel = mViewModelStore.get(key); //from cache Find out if there is an existing ViewModel object.
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass); //Create ViewModel object , And then cache it.
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
The ViewModelProvider.get() method is relatively simple, as stated in the comments. Finally, we look at the specific implementation of the ViewModelStore class
public class ViewModelStore {
private final HashMap mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.get(key);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
mMap.put(key, viewModel);
}
final ViewModel get(String key) {
return mMap.get(key);
}
public final void clear() {
for (ViewModel vm: mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
ViewModelStore is a class for caching ViewModel. The put() and get() methods are used to access ViewModel objects. In addition, a clear() method is provided to clear cached ViewModel objects. In this method, ViewModel.onCleared is called The () method informs the ViewModel that the object is no longer used.
ViewModel received onCleared() notification
HolderFragment's onDestroy() method
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
The ViewModelStore.clear() method is called in the onDestroy() method, and we know that the onCleared() method of the ViewModel will be called in this method. After you read the HolderFragment source code, you may have a question, where are the ViewModel objects stored in mViewModelStore added? If you are careful, you will find that in the ViewModelProvider construction method, the reference of the ViwModelStore object mViewModelStore in the HolderFragment has been passed to the mViewModelStore in the ViewModelProvider, and the ViewModel object will be added to the mViewModelStore in the ViewModelProvider.get() method.
Summary
ViewModel's responsibility is to manage and request data for Activity or Fragment. The specific data request logic should not be written in the ViewModel, otherwise the ViewModel's responsibilities will become too heavy. Here we need to introduce a Repository to be responsible for data request related work . For details, please refer to Android Architecture Components.
ViewModel can be used for the interaction of different Fragments within the Activity, and can also be used as a decoupling method between Fragments.
ViewModel can also be responsible for handling part of the Activity/Fragment interaction with other modules of the application.
The ViewModel life cycle (taking Activity as an example) starts at the first onCreate() of the Activity and ends at the final finish of the Activity.
Official:
https://developer.android.google.cn/topic/ libraries/architecture/viewmodel.html
————————————————
Copyright statement: This article is the original article of the CSDN blogger "Double-Smile", following the CC 4.0 BY-SA copyright agreement, please attach Link to the original source and this statement.
Link to the original text: https://blog.csdn.net/qq_24442769 /article/details/79426609
Overview
ViewModel, literally, it must be related to the view (View) and the data (Model). Just like its literal meaning, it is responsible for preparing and managing data classes related to UI components (Fragment/Activity), which means that ViewModel is used to manage For UI-related data, ViewModel can also be used for communication between UI components.
Previous problems
ViewModel is used to store and manage UI-related data. It can abstract the data logic related to an Activity or Fragment component and adapt to the life cycle of the component. For example, when the screen rotates and the Activity is rebuilt, the ViewModel The data is still valid.
Before the introduction of ViewModel, there were several problems as follows:
Usually the Android system manages the life cycle of UI controllers (such as Activity and Fragment). The system responds to user interaction or rebuilds components, and the user cannot control it. When the component is destroyed and rebuilt, the data related to the original component will also be lost. If the data type is relatively simple and the amount of data is not large, you can store the data through onSaveInstanceState(), and read the Bundle from it through onCreate() after the component is rebuilt. Data recovery. However, if it is a large amount of data that is inconvenient to serialize and deserialize, the above method will not be applicable.
UI controllers often send many asynchronous requests. It is possible that UI components have been destroyed but the request has not yet returned. Therefore, UI controllers need to do extra work to prevent memory leaks.
When the Activity is destroyed and rebuilt due to configuration changes, the general data will be re-requested. In fact, this is a waste. It is best to keep the last data.
UI controllers actually only need to be responsible for displaying UI data, responding to user interactions and system interactions.但往往开发者会在Activity或Fragment中写许多数据请求和处理的工作,造成UI controllers类代码膨胀,也会导致单元测试难以进行。我们应该遵循职责分离原则,将数据相关的事情从UI controllers中分离出来。
ViewModel基本使用
public class MyViewModel extends ViewModel {
private MutableLiveData> users;
public LiveData> getUsers() {
if (users == null) {
users = new MutableLiveData>();
loadUsers();
}
return users;
}
private void loadUsers() {
// 异步调用获取用户列表
}
}
新的Activity如下:
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// 更新 UI
});
}
}
如果Activity被重新创建了,它会收到被之前Activity创建的相同MyViewModel实例。当所属Activity终止后,框架调用ViewModel的onCleared()方法清除资源。
因为ViewModel在指定的Activity或Fragment实例外存活,它应该永远不能引用一个View,或持有任何包含Activity context引用的类。如果ViewModel需要Application的context(如获取系统服务),可以扩展AndroidViewmodel,并拥有一个构造器接收Application。
在Fragment间共享数据
一个Activity中的多个Fragment相互通讯是很常见的。之前每个Fragment需要定义接口描述,所属Activity将二者捆绑在一起。此外,每个Fragment必须处理其他Fragment未创建或不可见的情况。通过使用ViewModel可以解决这个痛点,这些Fragment可以使用它们的Activity共享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 onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends LifecycleFragment {
public void onActivityCreated() {
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// update UI
});
}
}
注意:上面两个Fragment都用到了如下代码来获取ViewModel,getActivity()返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个SharedViewModel对象。
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
1
这种方式的好处包括:
Activity不需要做任何事情,也不需要知道通讯的事情
Fragment不需要知道彼此,除了SharedViewModel进行联系。如果它们(Fragment)其中一个消失了,其余的仍然能够像往常一样工作。
每个Fragment有自己的生命周期,而且不会受其它Fragment生命周期的影响。事实上,一个Fragment替换另一个Fragment,UI的工作也不会受到任何影响。
ViewModel的生命周期
ViewModel对象的范围由获取ViewModel时传递至ViewModelProvider的Lifecycle所决定。ViewModel始终处在内存中,直到Lifecycle永久地离开—对于Activity来说,是当它终止(finish)的时候,对于Fragment来说,是当它分离(detached)的时候。
这里写图片描述
上图左侧为Activity的生命周期过程,期间有一个旋转屏幕的操作;右侧则为ViewModel的生命周期过程。
一般通过如下代码初始化ViewModel:
viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);
1
this参数一般为Activity或Fragment,因此ViewModelProvider可以获取组件的生命周期。
Activity在生命周期中可能会触发多次onCreate(),而ViewModel则只会在第一次onCreate()时创建,然后直到最后Activity销毁。
ViewModel相关类图
借用Android架构组件(三)——ViewModel的类图:
这里写图片描述
ViewModelProviders是ViewModel工具类,该类提供了通过Fragment和Activity得到ViewModel的方法,而具体实现又是由ViewModelProvider实现的。
ViewModelProvider是实现ViewModel创建、获取的工具类。在ViewModelProvider中定义了一个创建ViewModel的接口类——Factory。ViewModelProvider中有个ViewModelStore对象,用于存储ViewModel对象。
ViewModelStore是存储ViewModel的类,具体实现是通过HashMap来保存ViewModle对象。
ViewModel是个抽象类,里面只定义了一个onCleared()方法,该方法在ViewModel不在被使用时调用。ViewModel有一个子类AndroidViewModel,这个类是便于要在ViewModel中使用Context对象,因为我们前面提到不能在ViewModel中持有Activity的引用。
ViewModelStores是ViewModelStore的工厂方法类,它会关联HolderFragment,HolderFragment有个嵌套类——HolderFragmentManager。
ViewModel相关时序图
追溯创建一个ViewModel的源码,会察觉需要的步骤有点多。下面以在Fragment中得到ViewModel对象为例看下整个过程的时序图。
借用Android架构组件(三)——ViewModel的时序图:
这里写图片描述
时序图看起来比较复杂,但是它只描述了两个过程:
得到ViewModel对象。
HolderFragment被销毁时,ViewModel收到onCleared()通知。
ViewModel相关源码分析
ViewModelProviders类的具体实现:
public class 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;
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(checkActivity(fragment)));
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(activity));
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
checkApplication(checkActivity(fragment));
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@NonNull Factory factory) {
checkApplication(activity);
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
ViewModelProviders提供了四个of()方法,四个方法功能类似,其中of(FragmentActivity activity, Factory factory)和of(Fragment fragment, Factory factory)提供了自定义创建ViewModel的方法。
1. 判断Fragment的是否Attached to Activity,Activity的Application对象是否为空。
2. 创建ViewModel对象看似很简单,一行代码搞定。
new ViewModelProvider(ViewModelStores.of(fragment), factory)
先看看ViewModelStores.of()方法:
@NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) fragment).getViewModelStore();
}
return holderFragmentFor(fragment).getViewModelStore();
}
继续深入发现其实是实现了一个接口:
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
holderFragmentFor()是HolderFragment的静态方法,HolderFragment继承自Fragment。我们先看holderFragment()方法的具体实现
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
继续看HolderFragmentManager.holderFragmentFor()方法的具体实现
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;
}
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);
}
}
};
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment(); // 创建HolderFragment对象
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
public HolderFragment() {
//这个是关键,这就使得Activity被recreate时,Fragment的onDestroy()和onCreate()不会被调用
setRetainInstance(true);
}
setRetainInstance(boolean) 是Fragment中的一个方法。将这个方法设置为true就可以使当前Fragment在Activity重建时存活下来, 如果不设置或者设置为 false, 当前 Fragment 会在 Activity 重建时同样发生重建, 以至于被新建的对象所替代。
在setRetainInstance(boolean)为true的 Fragment 中放一个专门用于存储ViewModel的Map, 自然Map中所有的ViewModel都会幸免于Activity重建,让Activity, Fragment都绑定一个这样的Fragment, 将ViewModel存放到这个 Fragment 的 Map 中, ViewModel 组件就这样实现了。
到此为止,我们已经得到了ViewStore对象,前面我们在创建ViewModelProvider对象是通过这行代码实现的new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory)现在再看下ViewModelProvider的构造方法
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
现在就可以通过ViewModelProvider.get()方法得到ViewModel对象,继续看下该方法的具体实现
@NonNull
@MainThread
public extends ViewModel> T get(@NonNull String key, @NonNull Class modelClass) {
ViewModel viewModel = mViewModelStore.get(key); //从缓存中查找是否有已有ViewModel对象。
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass); //创建ViewModel对象,然后缓存起来。
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
ViewModelProvider.get()方法比较简单,注释中都写明了。最后我们看下ViewModelStore类的具体实现
public class ViewModelStore {
private final HashMap mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.get(key);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
mMap.put(key, viewModel);
}
final ViewModel get(String key) {
return mMap.get(key);
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
ViewModelStore是缓存ViewModel的类,put()、get()方法用于存取ViewModel对象,另外提供了clear()方法用于清空缓存的ViewModel对象,在该方法中会调用ViewModel.onCleared()方法通知ViewModel对象不再被使用。
ViewModel收到onCleared()通知
HolderFragment的onDestroy()方法
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
在onDestroy()方法中调用了ViewModelStore.clear()方法,我们知道在该方法中会调用ViewModel的onCleared()方法。在你看了HolderFragment源码后,或许你会有个疑问,mViewModelStore保存的ViewModel对象是在哪里添加的呢? 细心的话,你会发现在ViewModelProvider的构造方法中,已经将HolderFragment中的ViwModelStore对象mViewModelStore的引用传递给了ViewModelProvider中的mViewModelStore,而在ViewModelProvider.get()方法中会向mViewModelStore添加ViewModel对象。
总结
ViewModel职责是为Activity或Fragment管理、请求数据,具体数据请求逻辑不应该写在ViewModel中,否则ViewModel的职责会变得太重,此处需要一个引入一个Repository,负责数据请求相关工作。具体请参考 Android架构组件。
ViewModel可以用于Activity内不同Fragment的交互,也可以用作Fragment之间一种解耦方式。
ViewModel也可以负责处理部分Activity/Fragment与应用其他模块的交互。
ViewModel生命周期(以Activity为例)起始于Activity第一次onCreate(),结束于Activity最终finish时。
官方:
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html
————————————————
版权声明:本文为CSDN博主「Double-Smile」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_24442769/article/details/79426609