MVC, MVP and MVVM architecture pattern & ViewModel

Posted by tablex on Mon, 31 Jan 2022 19:32:25 +0100

Before learning viewModel, we need to understand MVC, MVP and MVVM architecture patterns.

A simple understanding can be viewed Illustration of MVC, MVP and MVVM.

Another super good article: Design architecture of Android App: MVC,MVP,MVVM and architecture.

MVC

Illustration:

It is the abbreviation of model view controller. It is a software design model. It organizes code by separating business logic, data and interface display. It does not need to rewrite business logic while improving and customizing interface and user interaction.

The current popular MVC framework is also used in the interface part of Android. In Android:

View: generally, XML files are used to describe the interface.

Control layer: the responsibility of Android's control layer usually falls on the shoulders of many activities. This sentence implies not to write code in the Activity, but to process it through the Activity delivery Model business logic layer. Another reason for this is that the response time of the Activity in Android is 5s. If time-consuming operations are put here, the program is easy to be recycled.

Model layer: the data structure and related classes established for the business model can be understood as the model of android app. The model is not related to View, but related to business. The operation of database and network should be handled in the model. Of course, the operation of business calculation must also be placed in this layer.

Disadvantages of MVC: in Android development, Activity is not a Controller in the standard MVC mode. Its primary responsibility is to load the application layout and initialize the user interface, accept and process the operation requests from users, and then respond. With the increasing complexity of the interface and its logic, the responsibilities of the Activity class are increasing, so that it becomes huge and bloated.

MVP

In the process of App development, the common problem is that the amount of code in a certain part is too large. Although module division and interface isolation are done, it is difficult to completely avoid it. Because the Activity itself needs to be responsible for the operation and interaction with users, and the display of the interface is not a simple Controller or View. Therefore, MVP framework is introduced. The MVP diagram is as follows:

MVP evolved from the earlier MVC framework and has certain similarities with MVC: Controller/Presenter is responsible for logic processing, Model provides data, and View is responsible for display.

View: responsible for drawing UI elements (generally including classes directly related to UI such as Activity, Fragment and Adapter) and interacting with users (embodied as Activity in Android)

Model: responsible for storing, retrieving and manipulating data (sometimes a Model interface is implemented to reduce coupling)

Presenter: as the intermediate link between View and Model, it handles the responsible logic of user interaction.

View interface: the interface that needs to be implemented by view. View interacts with Presenter through view interface to reduce coupling and facilitate unit testing

Necessity of View interface: recall how you unit tested the code logic when developing Android applications? Do you want to deploy the application to Android simulator or real machine every time, and then test it by simulating user operation? However, due to the characteristics of Android platform, each deployment takes a lot of time, which directly leads to the reduction of development efficiency. In MVP mode, presenters dealing with complex logic interact with View(Activity) through interface, which shows that we can implement this interface through custom classes to simulate the behavior of Activity and unit test presenters, saving a lot of deployment and testing time.

When we move the complex logic processing of Activity to another class (Presenter), Activity is actually the View in MVP mode. It is responsible for initializing UI elements and establishing the association between UI elements and Presenter (Listener and so on). At the same time, it will also process some simple logic (complex logic is handled by Presenter).

The Presenter of MVP is the Controller of the framework and undertakes a large number of logical operations, while the Controller of MVC often undertakes the role of forwarding. Therefore, the reason why MVP is introduced into the App is to put a large number of logical operations previously contained in the Activity into the control layer to avoid the bloated Activity.

Main differences between the two modes:

(the main difference) View and Model do not interact directly, but interact indirectly with Model through interaction with Presenter. In MVC, View can interact with Model directly
Usually, View and Presenter are one-to-one, but complex View may bind multiple presenters to process logic. The Controller is behavior based and can be shared by multiple views. The Controller can decide which View to display
The interaction between Presenter and View is carried out through the interface, which is more conducive to adding unit tests.

Therefore, we can find the advantages of MVP as follows:

1. The model is completely separated from the view. We can modify the view without affecting the model;

2. The model can be used more efficiently because all interactions take place in one place - inside the Presenter;

3. We can use one Presenter for multiple views without changing the Presenter's logic. This feature is very useful because the view changes more frequently than the model;

4. If we put the logic in the Presenter, we can test the logic (unit test) away from the user interface.

In MVP, the code of Activity is not bloated;
In MVP, the change of model (implementation class of iusermodel) will not affect the Activity(View), and they will not interfere with each other, but in MVC;
In MVP, IUserView can test Presenter conveniently;
In MVP, UserPresenter can be used for multiple views, but Activity in MVC cannot.

MVVM

MVVM can be regarded as an upgraded version of MVP, in which VM is the abbreviation of ViewModel, which can be understood as the combination of View data model and Presenter. The interaction between ViewModel and View is completed through Data Binding, and Data Binding can realize two-way interaction, which further reduces the coupling degree between View and control layer and separates concerns more thoroughly, At the same time, it reduces the pressure of Activity. The diagram is as follows:

MVVM mode changes the name of presenter to View Model, which is basically the same as MVP mode. The only difference is that it adopts two-way data binding. Changes in View are automatically reflected in ViewModel, and vice versa.

Therefore, ViewModel appears here.

ViewModel

The emergence of MVVM in Android is Jetpack series, which mainly uses the combination of LiveData, ViewModel and data binding to realize MVVM.

We know that changes in configuration items such as rotating the screen will lead to the destruction and reconstruction of our Activity. At this time, the data held by the Activity will be lost, while the ViewModel will not be destroyed, which can help us save the data in this process rather than re obtain it after the reconstruction of the Activity. Moreover, ViewModel allows us not to worry about potential memory leakage. At the same time, ViewModel has more advantages than onSaveInstanceState() method, such as storing relatively large data, and does not need serialization and deserialization.

The life cycle diagram of ViewModel is:

As can be seen from the above figure, the ViewModel is still alive when the Activity is rebuilt. Why?

The main business scenarios for using ViewModel are:

When we rotate the screen without other processing, because the screen rotation activity is destroyed and rebuilt, the data stored in the activity will naturally be lost. This will not happen if you use ViewModel.

For example, create a new myviewmodel Java, as follows:

public class MyViewModel extends ViewModel {
    public int number = 0;
}

The code in the main Activity is as follows:

public class MainActivity extends AppCompatActivity {
    Button button;
    MyViewModel myViewModel;
    TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);
        myViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);
        textView.setText(String.valueOf(myViewModel.number));
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myViewModel.number++;
                textView.setText(String.valueOf(myViewModel.number));
            }
        });
    }
}

When the screen is flipped, the data will not be lost.

So how is ViewModel implemented?

ViewModel internal implementation

And how ViewModel updates data.

myViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);

Get the ViewModel instance object you need through the get method of the ViewModelProvider instance.

Then myviewmodel number++; How to complete the update and save during the operation? Let's take a look at how our ViewModel object is obtained:

public <T extends ViewModel> T get(@NonNull Class<T> 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);
}

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> 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;
}

After the viewModel is instantiated, the ViewModelStore is used for storage. Use NewInstanceFactory when creating for the first time, as follows:

public static class NewInstanceFactory implements Factory {

    @SuppressWarnings("ClassNewInstance")
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            return modelClass.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        }
    }
}

You can see that here is the instance object obtained by reflection. Then through mviewmodelstore put(key, viewModel); Store the object, and put is implemented as follows:

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);
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

In the ViewModelStore class, HashMap is used for storage, and the key is the aforementioned DEFAULT_KEY + ":" + canonicalName, i.e. DEFAULT_KEY + ":" plus fully qualified class name.

At this point, we know how the ViewModel obtains the instance object and how to store it. But I still don't know how the object updates data. So we need to be more careful.

We re observe:

new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);

Only the construction method of the ViewModelProvider class is left. If you don't understand it, you might as well take a look at the following:

public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
    this(owner.getViewModelStore(), factory);
}

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    this.mViewModelStore = store;
}

In other words, the key is how to get the ViewModelStore. That is, how the ViewModelStoreOwner class is written. be aware:

public interface ViewModelStoreOwner {
    ViewModelStore getViewModelStore();
}

If ViewModelStoreOwner is an interface and this object is passed in, the corresponding implementation must be made in a parent class of Activity. It is easy to trace the implementation of this interface in FragmentActivity:

public class FragmentActivity extends SupportActivity implements ViewModelStoreOwner

Then we can find the implementation of getViewModelStore method corresponding to this interface:

public ViewModelStore getViewModelStore() {
    if (this.getApplication() == null) {
        throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call.");
    } else {
        if (this.mViewModelStore == null) {
            FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
            if (nc != null) {
                this.mViewModelStore = nc.viewModelStore;
            }

            if (this.mViewModelStore == null) {
                this.mViewModelStore = new ViewModelStore();
            }
        }

        return this.mViewModelStore;
    }
}

If it does not exist, instantiate it using the parameterless construction of ViewModelStore.

An interesting thing is that when I tracked the viewModel class, I found a use case here, which is different from what I used:

public class MyFragment extends Fragment {
    public void onStart() {
    UserModel userModel = 	ViewModelProviders.of(getActivity()).get(UserModel.class);
    }
}

ViewModelProviders is used instead of ViewModelProvider. Then I was at mainactivity Corresponding replacement is made in the java file:

//        myViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);
myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);

It turned out to be the same.

Notice that my version here is: implementation 'Android arch. Lifecycle: Extensions: 1.1.1 ', the ViewModelProviders method was abandoned in 2.2.0.

However, how does ViewModel update data? I really didn't see this. This question will be considered later.

Topics: ViewModel