Android Jetpack components (three) ViewModel

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, Activitythey 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 Activityrebuilt, it will not affect the life cycle of ViewModel.
ViewModel life cycle
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(). ActivityThis 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 Activityreconstruction 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 ViewModelProvidercompleted. 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

ActivityTwo or more Fragmentoften need to communicate with each other, this process is more complex, data can ViewModel from Activitythe release treatment, as long as Activitynot to destroy, ViewModel have existed, based on these characteristics, a plurality Fragmentthereof may be used Activityrange shared by ViewModel Handle this type of communication.

 //ViewModelProvider的范围必须是所在activity
 HomeViewModel homeViewModel = new ViewModelProvider(getActivity()).get(HomeViewModel.class);

The FragmentViewModel is also instantiated in the other . When switching Fragment, it will prompt the current value of the counter to reach the Fragmentcommunication 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 Fragmentuse ViewModel and Activityused similar.

Principle of ViewModel

ViewModel receives an ViewModelStoreOwnerobject as a parameter, we pass this, this is because of Activityinheritance ComponentActivity, it implements the ViewModelStoreOwnerinterface 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 ViewModelStoreand 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.
FragmentThe ViewModelStoreOwnerinterface is also implemented by default , and the principle is Activitysimilar.
It should be noted that when instantiating the ViewModel, do not pass in any type Contextor Contextreferenced 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 Contextan object, you can use AndroidViewModelthe class that inherits from ViewModel, and receives Applicationas Context, its life cycle and so use Applicationthe same life cycle, does not lead to a memory leak, while treatment of data in a specific scene.

Guess you like

Origin blog.csdn.net/yang_study_first/article/details/115229934