An overview of the

This article continues with Jetpack, specifically the ViewModel that implements the MVVM architecture. This example will use ViewModel and LiveData together, and then analyze the ViewModel source code. LiveData will be analyzed in the next article.

In the MVP infrastructure setup example in the previous article, the MVP architecture was used to implement the network loading process. This time we will upgrade the MVP architecture to the MVVM, removing the Presenter and Model modules from the previous example and leaving only the network loading engine. MVVM is then implemented by combining ViewModel and LiveData.

1, use,

Lead-in dependency:

Implementation 'androidx. Lifecycle: lifecycle - livedata - KTX: 2.2.0' implementation 'androidx. Lifecycle: lifecycle - viewmodel - KTX: 2.2.0'Copy the code

We need to know that the ViewModel is an abstract class that we need to implement. So let’s create an implementation class:

public class DataViewModel extends ViewModel {
    // LiveData
    private MutableLiveData<String> mLiveData;
    // Network loading engine
    private RetrofitRequestInterface retrofit;
    public DataViewModel(a){
        // Comment 1, initialize LiveData
        mLiveData = new MutableLiveData();
        // Note 2, initialize the network engine
        retrofit = RetrofitInstance.getRetrofitInstance().create(RetrofitRequestInterface.class);
    }
    public LiveData getLiveData(a){
        return mLiveData;
    }
    public void getData(String url){
        // Note 3, network request
        Call<ResponseBody> call = retrofit.getRequest(url);
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                try {
                    String result = response.body().string();
                    // Comment 4, with LiveData callback
                    mLiveData.postValue(result);
                } catch(IOException e) { e.printStackTrace(); mLiveData.postValue(e.getMessage()); }}@Override
            public void onFailure(Call<ResponseBody> call, Throwable t) { mLiveData.postValue(t.getMessage()); }}); }}Copy the code

Above we created a ViewModel implementation class that acts somewhat like MVP’s Presenter. Comments 1 and 2 above create the LiveData object and the network engine, respectively, perform the network load on the local outside call in comment 3, and then load the results with the LiveData callback in comment 4.

Here’s how the interface is called:

public class MainActivity extends AppCompatActivity {
    private Button getData;
    private DataViewModel mViewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getData = findViewById(R.id.id_get_data);
        init();
    }
    private void init(a){
        // Note 5, Factory design pattern creates ViewModel objects
        mViewModel = new ViewModelProvider(this).get(DataViewModel.class);
        // Set the data callback with the LiveData object
        mViewModel.getLiveData().observe(this.new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Toast.makeText(getApplicationContext(), "mag = "+ s, Toast.LENGTH_SHORT).show(); }}); getData.setOnClickListener((View view) -> {// Network request
            mViewModel.getData("https://www.baidu.com"); }); }}Copy the code

The above steps create the ViewModel object, set up the data callback, and execute the network request. This is the basic implementation of MVVM. Demo: the ViewModel

Let’s start with two questions:

1. Why use ViewModelProvider to create a ViewModel object instead of creating a new one? ViewModel can avoid memory leaks.

Let’s analyze the ViewModel source code.

2. Source code analysis

See the ViewModelProvider constructor and get() method above:

// ViewModelProvider.java
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
 / / comment 6, obtained by owner ViewModelStore getDefaultViewModelProviderFactory factory object
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : ViewModelProvider.NewInstanceFactory.getInstance());
    }
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull ViewModelProvider.Factory factory) {
        // Note 7, save Factory Factory and cache object, ViewModelStore
        mFactory = factory;
        mViewModelStore = store;
    }
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) { String canonicalName = modelClass.getCanonicalName(); .return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        // Note 9, fetch the ViewModel object from the cache mViewModelStore
        / / the Key value is "androidx. Lifecycle. ViewModelProvider. DefaultKey" + name of the class
        ViewModel viewModel = mViewModelStore.get(key);
        if (modelClass.isInstance(viewModel)) {
            ......
            // Objects of the response class are returned directly
            return(T) viewModel; }... .// Note 10: There is no ViewModel object in the cache, create it with a factory and store it in the cache
        viewModel = (mFactory).create(modelClass);
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

Copy the code

In the constructor of the ViewModelProvider overload in comments 6 and 7 above, Call the Activity respectively getViewModelStore () and getDefaultViewModelProviderFactory () method to obtain or create a ViewModel object cache object ViewModelStore and factory Factory and save it. Inside the cache object ViewModelStore is a HashMap that caches ViewModel objects as key-value pairs.

Before creating a ViewModel object, the ViewModelProvider retrieves it from the cache ViewModelStore. The key value is “androidx. Lifecycle. ViewModelProvider. DefaultKey” + name of the class. When the object is not in the cache, it is created with the Factory, cached and returned.

Factory is an abstract class, implementation class object from the upper 6 through Activity getDefaultViewModelProviderFactory () method. Let’s look at how this method for ComponentActivity creates an object for the factory implementation class:

   // ComponentActivity.java
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory(a) {...if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    / / comment 11, access to the Application object, create SavedStateViewModelFactory object
                    getApplication(),
                    this, getIntent() ! =null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }

    // SavedStateViewModelFactory.java
    public SavedStateViewModelFactory(@NonNull Application application,
                                      @NonNull SavedStateRegistryOwner owner,
                                      @Nullable Bundle defaultArgs) {
        mSavedStateRegistry = owner.getSavedStateRegistry();
        mLifecycle = owner.getLifecycle();
        mDefaultArgs = defaultArgs;
        mApplication = application;
        // Note 12, Create AndroidViewModelFactory
        mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {...// Note 13, create a ViewModel object from AndroidViewModelFactory
        returnmFactory.create(modelClass); . }Copy the code

Above comments at 11, ComponentActivity gained the Application object, created a subclass of the Factory SavedStateViewModelFactory object. Note 12 SavedStateViewModelFactory then to application another as a parameter to create a Factory implementation class AndroidViewModelFactory object, create ViewModel object and the object.

AndroidViewModelFactory AndroidViewModelFactory

    public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
        private static ViewModelProvider.AndroidViewModelFactory sInstance;
        @NonNull
        public static ViewModelProvider.AndroidViewModelFactory getInstance(@NonNull Application application) {
            // Note 14, singleton pattern to create factory instances
            if (sInstance == null) {
                sInstance = new ViewModelProvider.AndroidViewModelFactory(application);
            }
            return sInstance;
        }
        private Application mApplication;
        // Pass in the Application object
        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                try {
                    // Note 15, The factory method design pattern creates a ViewModel object that holds a reference to Application
                    returnmodelClass.getConstructor(Application.class).newInstance(mApplication); }... }return super.create(modelClass); }}Copy the code

The factory named AndroidViewModelFactory above is created using the singleton pattern in note 14, and then the ViewModel object is created using the factory method design pattern in note 15. It is worth noting that the ViewModel is created with the mApplication parameter passed in. That is, the ViewModel object does not end up holding a reference to the Activity, but to the Application.

After the above analysis, we should now be able to answer the above two questions:

1. The ViewModel object is obtained using the ViewModelProvider instead of new directly, because the ViewModelProvider has a caching mechanism, and internally uses HashMap to cache the ViewModel object in the form of key-value pairs. A factory is used to create objects that are not in the cache.

2. The ViewModel object does not hold a reference to the Activity, but to the Application. This is one way to avoid Activity memory leaks.

At the end

If you study with me, you can pay attention to my public account, ❤️ program ape Development Center ❤️, and we will share technology regularly every week. Join me and learn together!