Android Jetpack component series articles:
Android Jetpack component (1) LifeCycle
Android Jetpack component (2) Navigation
Android Jetpack component (3) ViewModel
Android Jetpack component (4) LiveData
Android Jetpack component (5) Room
Android JetPack component (6) DataBinding
Android Jetpack Components (7) Paging
Android Jetpack Components (8) WorkManager
First language
For applications that support horizontal and vertical screen switching, when we switch between horizontal and vertical screens, Activity
they will be re-created. We need to consider data storage and recovery. Jetpack provides us with ViewModel components to help us solve this problem. ViewModel stores and manages interface-related data in a life cycle-oriented way. ViewModel is independent of configuration changes, even if it is Activity
rebuilt, it will not affect the life cycle of ViewModel.
In application development, business logic such as UI interaction and data acquisition is usually written on the page. When project requirements continue to increase and page functions are complex, the page class will appear particularly bloated and not suitable for maintenance. This also violates the "single function principle". The page should only be responsible for handling the interaction between the user and the UI controls and data display, and the business logic for obtaining data should be handled separately.
Android provides the ViewModel class specifically for storing the data required by the application page. It can be understood as a bridge between the view and the data model, which separates the view from the data while maintaining communication.
ViewModel与onSaveInstanceState()
Usually we use onSaveInstanceState()
to solve the problem of data loss caused by screen rotation, but it can only save a small amount of data that supports serialization, and Viewmodel supports all data in the page. It should be noted that the ViewModel does not support the persistence of data. When the interface is completely destroyed, the ViewModel and the data it holds will no longer exist. onSaveInstanceState()
Without this restriction, the data of the page can be persisted, and the two purposes are different.
rely
//包含了 viewmodel 和 livedata,lifecycle-extensions 中的 API 已弃用
//implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
//或者指明使用viewmodel
implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0"
use
First, customize a ViewModel and inherit the ViewModel class.
public class HomeViewModel extends ViewModel {
@Override
protected void onCleared() {
super.onCleared();
}
}
ViewModel is an abstract class, and there is only one onClear()
. Activity
This method will be called when the ViewModel associated with it is destroyed. Some work related to resource release can be performed in the method. Note that the Activity
reconstruction caused by screen rotation will not call this method. .
We use a counter example to show the use of ViewModel to separate the view from the data.
private int value;
public String gtValue() {
return String.valueOf(value);
}
public void addValue() {
value+=1;
if (onChangedListener != null) {
onChangedListener.onChanged(String.valueOf(value));
}
}
/**
* 通过接口的形式将数据传递出去,更好的方式是通过LiveData,
*/
public interface onChangedListener {
void onChanged(String value);
}
private onChangedListener onChangedListener;
public void setOnChangeListener(onChangedListener onChangeListener) {
this.onChangedListener = onChangeListener;
}
Then Activity
, every time you click, the counter is +1, and the instantiation of the ViewModel is ViewModelProvider
completed. It will determine whether the ViewModel exists, if it exists, return directly, and create it if it does not exist.
HomeViewModel homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
textView.setText(homeViewModel.gtValue());
button.setOnClickListener(view -> homeViewModel.addValue());
homeViewModel.setOnChangeListener(textView::setText);
Running the code can find that the click counter will be +1, and the data will not disappear when the activity is rebuilt by rotating the screen, which means that the ViewModel has not been destroyed and the held data has always existed.
Sharing data between fragments
Activity
Two or more Fragment
often need to communicate with each other, this process is more complex, data can ViewModel from Activity
the release treatment, as long as Activity
not to destroy, ViewModel have existed, based on these characteristics, a plurality Fragment
thereof may be used Activity
range shared by ViewModel Handle this type of communication.
//ViewModelProvider的范围必须是所在activity
HomeViewModel homeViewModel = new ViewModelProvider(getActivity()).get(HomeViewModel.class);
The Fragment
ViewModel is also instantiated in the other . When switching Fragment
, it will prompt the current value of the counter to reach the Fragment
communication between.
homeViewModel = new ViewModelProvider(getActivity()).get(HomeViewModel.class);
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
Toast.makeText(getActivity(), homeViewModel.gtValue(),Toast.LENGTH_SHORT).show();
}
In Fragment
use ViewModel and Activity
used similar.
Principle of ViewModel
ViewModel receives an ViewModelStoreOwner
object as a parameter, we pass this, this is because of Activity
inheritance ComponentActivity
, it implements the ViewModelStoreOwner
interface by default . The source code is as follows.
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
The responsibility of implementing this interface is to retain ownership during configuration changes ViewModelStore
and to be called when the scope is about to be destroyed.
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner {
public ComponentActivity() {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
}
getViewModelStore()
The type of return is ViewModelStore
. As can be seen from the source code, the ViewModel is HashMap<String,ViewModel>
cached in the form of. As mentioned before, when the page needs the ViewModel, first determine whether it exists in the cache, if it exists, return directly, and create it if it does not exist.
Fragment
The ViewModelStoreOwner
interface is also implemented by default , and the principle is Activity
similar.
It should be noted that when instantiating the ViewModel, do not pass in any type Context
or Context
referenced object, which will cause a memory leak.
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
AndroidViewModel
If the instantiation ViewModel to pass Context
an object, you can use AndroidViewModel
the class that inherits from ViewModel, and receives Application
as Context
, its life cycle and so use Application
the same life cycle, does not lead to a memory leak, while treatment of data in a specific scene.