preface

Those who do mobile terminal development and front-end development should be familiar with the terms MVC, MVP, and MVVM. These are the three most commonly used application architecture patterns. The purpose is to separate the implementation code of business and view, so that the same application can be used in different forms. However, there is a wide range of online articles on this subject, many of which are misdescribed, leading some people to fall into the trap of applying these architectural patterns. This article will go back to the source and try to give people a correct understanding of these three architectural patterns.

MVC = model-view-controller, MVP = model-view-presenter, MVVM = model-view-viewModel. Each of these three architectural patterns has three different components, and all have the same Model and View layers. Model is the Model layer, which mainly manages the data and behavior of the business Model. View is the presentation layer whose responsibility is to manage the user interface. All three architectural patterns are designed to decouple models and views. The main difference is that the solutions for decoupling are different. Controller, Presenter and ViewModel correspond to three different decoupling schemes, three connection modes with M and V.

Now, let’s start with MVC, and once we get MVC right, the other two are pretty easy to understand.

MVC

MVC was originally used in Smalltalk-80, One of the earliest references to MVC came from a paper called Applications Programming in Smalltalk-80(TM):How to Use Model-View-Controller (MVC) back in 1979. In this paper, the three modules of M-V-C and the communication between them are described in some design details.

In MVC, applications are divided into three roles: Model, View, and Controller. The three have their own specific purposes and responsibilities, and through mutual communication with each other to achieve program functions. For those of you who don’t know much about MVC, there are questions like, is a Model equal to an entity class? Does the business logic belong to the Controller or the Model? How exactly do the Model and View communicate? These are questions you will find answers to below.

The hardest part of the MVC trifle to understand is the Model, so let’s break it down first. As we said earlier, the Model layer mainly manages the data and behavior of the business Model. It both holds the data of the program and defines the logic to process that data, so Model = data + business logic. Therefore, processing the business logic is the responsibility of the Model, not the Controller. From the dimension of data, it can be divided into data definition, data storage and data acquisition. The definition of data is essentially the definition of data structures, usually defined in entity classes to facilitate the transfer of data between different roles. Data can be stored and retrieved in several ways: databases, networks, or caches. In practice, therefore, a Model is not simply an object, but a broader hierarchy. In many cases, the Model layer can be broken down again. For example, when applied to a client program, the Model layer can be further divided into business logic layer, network layer, storage layer, etc., and the entity class is just a data structure that runs through it. However, in a narrow sense, when we talk about a Model object, we’re talking about external components, and we’re talking more about the data that the Model provides to the outside world, and we don’t care where the data comes from. This is probably why many people mistake Model for an entity class.

The View is the one that’s best understood in MVC, and it’s going to receive the user’s interaction request and present the data to the user. The data presented by a View may be part of a Model object, all of a Model object, or even a combination of data from multiple Model objects. In MVC, views are designed to be nested, using the Composite pattern. For example, a ListView or a TableView consists of each Item, which in turn can consist of pictures, text, buttons, and so on. Views tend to be reusable, so in practice, views tend to be developed as relatively generic components.

The Controller layer acts as a bridge between the Model and View to control the flow of the program. The Controller is responsible for ensuring that the View has access to the Model object data that needs to be displayed and acts as a conduit for the View to learn about changes to the Model. When a View receives an interactive request from a user, it forwards the request to the Controller, which resolves the request and gives it to the corresponding Model for processing. So, in theory, the Controller should be very light.

MVC communication mechanism

In fact, since the development of MVC, the communication mechanism between the three pieces has evolved into a variety of communication paths. Let’s take a look at the original version:

This version of MVC can actually be seen as a combination of three basic design patterns: the combination pattern, the policy pattern, and the observer pattern. The composite mode, as mentioned earlier, is used in the View layer. The Controller object implements the policy for one or more View objects. The View object is limited to maintaining its visual appearance, and all decisions related to program logic are delegated to the Controller. That is, a View can be implemented using different controllers to get different behaviors. The View registers as an observer to the Model, and notifies the View when the Model changes.

So, the general interaction process is:

  1. The user manipulates the View, which then generates an event. For example, when a user clicks a button, a click event is generated.
  2. The Controller object receives an event and interprets it, that is, it applies a policy. This policy can be a request for a Model object to change its state or a request for a View to change its behavior or appearance. For example, if an event generated by a registration button is received by the Controller, it will interpret the event, perhaps checking to see if the user’s input is empty, and if so, asking the View to prompt the user to fill in a user name and password. If the validation is successful, UserModel is asked to create a new user.
  3. When the state changes, the Model object notifies all objects that have registered as observers. If the observer is a View object, you can update its appearance or behavior accordingly. As in the previous example, when UserModel successfully creates a new user, it can notify observers. When the View object receives the notification that UserModel has successfully created a new user, it can jump to the page after the successful registration.

This is the communication mechanism of the original version of MVC, which has been widely used in GUI applications since it was introduced in 1979. Note that there was no HTTP back then. Later, with the emergence of Microsoft ASP.NET MVC Framework, MVC began to be widely used in Web applications.

Variations of the MVC

The previous versions of MVC actually reduced the reusability of the View because the View relied on the Model. So, if you can decouple the View from the Model completely, you can improve the reusability of the View. As a result, the following variant MVC appears:

Instead of directly communicating with the Model, the View and Model communicate with each other through the Controller. The Model tells the Controller the result, and the Controller updates the View.

As far as I know, Apple came up with this variant. I don’t know if anyone else came up with this variant before Apple. In addition, many Web frameworks are designed based on the MVC design idea of this variant pattern, such as SpringMVC framework. Of course, the actual implementation is much more complicated than this, but the main design idea is MVC.

This variant, many people will mistake it for another classic architectural pattern “three-tier architecture”, that is, they think MVC is a three-tier architecture. In fact, the two are different. The three layers architecture are: presentation layer, business logic layer and data access layer. Although the communication is similar to MVC, the responsibilities of each layer are different, and most importantly, the scope of use is different. The three-tier architecture is divided into three layers from the perspective of the overall application architecture, while MVC is just a design scheme that divides functions in the presentation layer. Therefore, if there is any connection between the two, it is also a subset of the three-tier architecture.

Next, let’s look at how MVC is structured in practice. In actual application, it is mainly used in App development. Take iOS as an example, see the following figure:

The point is that UIViewController, actually, not only does it act as a Controller in MVC, but it also does some of the work of the View. We can divide the View role into several parts according to different functions. One is responsible for the layout and rendering of the interface, the other is responsible for the life cycle management of the interface, and the third is responsible for the data filling of the interface. These three functions, two and three are actually handled by the UIViewController, which not only acts as the Controller, but also manages the life cycle of the View and manages the data of the View. A stand-alone View is all that’s left of the interface presentation, and in iOS it’s mostly the XIB and Storyboard that does that.

An Android Activity is the same, acting as both a Controller and part of a View.

In addition, in an App, when the Controller requests data from the Model, it’s often time consuming, so the Model notifies the Controller asynchronously.

MVC summary

So just a little summary of MVC. MVC provides a groundbreaking design idea for the separation of business and View implementation, and makes the Model responsible for business logic and View responsible for display realize decoupling, so that the Model reuse is high, multiple views can share a Model, and, You can replace the View’s representation without modifying the Model.

In terms of interaction, early MVC views were directly dependent on models, so the reusability of views was actually limited. In addition, this model does not actually work for Web applications that are separated from the front and back ends. Therefore, a variety of MVC has been developed to cut off the direct dependence of View and Model, and schedule through Controller in a unified way, thus improving the reusability of View, as well as the MVC extension can also be applied to Web applications that are separated from the front and back ends.

However, in the actual application of App, it is a different kind of interaction structure. There is a ViewController role, which not only takes on the responsibilities of the Controller, but also takes on some of the responsibilities of the View, mainly including the life cycle management of the View and data filling, etc., while the original View role only has the function of display. The main advantage of this approach is that the View is lighter and more reusable; The disadvantages are obvious. The original Controller was very light, but now the ViewController is very heavy and takes on too much responsibility.

In addition, in many practical projects, the ViewController model actually has the side effect that when developers don’t understand MVC very well, they think that this is what an MVC Controller looks like. You will mistakenly move some code belonging to the View and Model to the ViewController, causing the already heavy ViewController to become even more bloated. As a result, MVC became a Massive View Controller, which deviated from the original purpose of MVC.

To address this problem at its root, many App projects have therefore switched to MVP or MVVM. Next, let’s look at MVP and MVVM.

MVP

The MVP Model was first explained in a 1996 paper called MVP: model-view-presenter The Taligent Programming Model for C++ and Java. The author of the paper was A man named Mike Potel, then VP & CTO of Taligent, a wholly owned subsidiary of IBM. However, if you read the paper, you will see that the original MVP was not the same as the MVP we know today. The MVP discussed in the paper actually has the following structure:

In fact, the MVP is based on the data management and user interface two dimensions of several problems, the Smalltalk version of MVC is decomposed and evolved into several intermediate components: Interactor, Commands, Selections. Interactor is best understood as the interactive event of a View. Commands defines operations on Model selected data, such as delete, modify, and save. Selections can be understood as filtering the Model data set and selecting a subset based on conditions. In addition, all components, including models, views, and presenters, interact with each other through interfaces.

If you ignore some of the intermediate components between the three pieces, you will see that the dependencies between them are the same as in the Smalltalk version of MVC. It is also mentioned in the paper that a Presenter is actually a Controller, just to distinguish it from MVC, so it is called a Presenter.

Let’s look at the MVP chart as we know it today:

If you look at this diagram, isn’t it exactly the same as the previous article about variant MVC? Yes, the diagram is the same, but the implementation and role division behind it are different, as we’ll see later. I don’t know how this model evolved, except that it has become the standard structure for MVPS today.

In MVP, the responsibilities and dependencies of the three pieces are the same as in variant MVC, but the interactions between the MVPS are mainly implemented through interfaces. The Model, View, and Presenter have their own interfaces that define their own behavior methods. Programming for interfaces naturally reduces coupling, improves reusability, and makes unit testing easier.

As we said before, MVC, UIViewController, and Activity in real applications are both controllers and part of the View, the responsibilities are not clear, As a result, UIViewController and Activity code is getting cluttered and bloated. In MVP mode, the UIViewController and Activity are clearly divided into View roles, and the responsibilities of the Controller role are transferred to a Presenter. The responsibilities are clear, and the code is easy to clear.

Simple use of MVP

Let’s use a simple login example to illustrate how to use MVP. Here is the class diagram for this example:

Four interfaces and three implementation classes are defined. LoginActivity is an Android Activity class, and in iOS, it can be defined as a LoginViewController. LoginActivity implements the LoginView interface and also holds a LoginPresenter object. LoginView and LoginActivity are clearly divided into the View layer. LoginView defines the interface methods of several UI layers involved in the login process, including the display and hiding of the loading box, the display of error information when the login fails, and the processing after the successful login. These methods are called in the LoginPresenterImpl method. The LoginPresenter layer defines two interfaces. LoginPresenter has only one login interface method, and OnLoginFinishedListener defines a callback method for login results. Both interfaces are implemented by LoginPresenterImpl. In addition, as you can see from the figure, LoginPresenterImpl holds both a LoginView object and a LoginModel object, LoginPresenterImpl is essentially a bridge between LoginView and LoginModel. The LoginModel in the Model layer does not directly hold a LoginPresenter object, but only adds a callback object parameter to the login interface.

LoginActivity

Next, let’s take a quick look at some key code implementations. Let’s first look at the key code for LoginActivity:

public class LoginActivity extends AppCompatActivity implements LoginView {
    private LoginPresenter loginPresenter;
  	private ProgressBar progressBar;
  	private LoginButton loginBtn;
  	private EditText userNameEdt;
  	private EditText passwordEdt;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
      	progressBar = findViewById(R.id.loading);
      	loginBtn = findViewById(R.id.login);
      	userNameEdt = findViewById(R.id.username);
      	passwordEdt = findViewById(R.id.password);
      
        loginPresenter = new LoginPresenterImpl();
      	loginPresenter.attachView(this);
      
      	loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) { loginPresenter.login(userNameEdt.getText().toString(), passwordEdt.getText().toString()); }}); }@Override
    public void showLoading(a) {
        if (!progressBar.isShowing()) {
            progressBar.show();
        }
    }
  
    @Override
    public void hideLoading(a) {
        if(progressBar.isShowing()) { progressBar.dismiss(); }}@Override
    public void onLoginError(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
  
  	@Override
    public void onLoginSuccess(a) {
        Toast.makeText(this."Success", Toast.LENGTH_SHORT).show();
      	startActivity(new Intent(this, MainActivity.class));
    }
  
  	@Override
    protected void onDestroy(a) {
        super.onDestroy(); loginPresenter.detachView(); }}Copy the code

The loginPresenter object is initialized in the onCreate() method, binds itself as a reference to the LoginView, and calls the loginPresenter. Login () method in the click event of the login button. This passes the login event to the loginPresenter.

LoginPresenterImpl

A Presenter is a bridge between the View and the Model. Let’s look at the key code for LoginPresenterImpl:

public class LoginPresenterImpl implements LoginPresenter.OnLoginFinishedListener {
  	private LoginView loginView;
  	private LoginModel loginModel;
  
  	public LoginPresenterImpl(a) {
      	this.loginModel = new LoginModelImpl();
    }
  
  	@Override
  	public void attachView(LoginView loginView) {
        this.loginView = loginView;
    }
    
  	@Override
    public void detachView(a) {
        this.loginView = null;
    }
    
  	@Override
    public boolean isViewAttached(a){
        returnloginView ! =null;
    }
  	
  	@Override
    public void login(String username, String password) {
      	loginView.showLoading();
      	loginModel.login(username, password, this);
    }
  
  	@Override
    public void onLoginError(String msg) {
      	if(isViewAttached()) { loginView.hideLoading(); loginView.onLoginError(msg); }}@Override
    public void onLoginSuccess(a) {
      	if(isViewAttached()) { loginView.hideLoading(); loginView.onLoginSuccess(); }}}Copy the code

LoginModelImpl

Finally, LoginModelImpl is the business logic for login, which is generally implemented through the network request server. Let’s look at the pseudo code:

public class LoginModelImpl implements LoginModel {
  	@Override
  	public void login(String username, String password, OnLoginFinishedListener listener) {
      	apiService.login(username, password)
          	.subscribeOn(Schedulers.newThread())
          	.observeOn(AndroidSchedulers.mainThread())
          	.subscribe(new Subscriber<Response>() {
            		@Override
            		public void onCompleted(a) {}@Override
            		public void onError(Throwable e) {
              			listener.onLoginError("Server Error")}@Override
            		public void onNext(Response res) {
              			if (res.isSuccess()) {
                      	listener.onLoginSuccess();
                    } else{ listener.onLoginError(res.getMsg()); }}}); }}Copy the code

The sample code uses Retrofit + RxJava, and the core code is in the onNext() method, which is called when the request response returns.

This concludes the simplest use case for MVP. In addition, since each business function basically has a corresponding set of MVP interfaces, many times this set of interfaces will be wrapped together to form a set of contracts, as shown below:

public interface LoginContract {
  	public interface LoginView {... }public interface LoginPresenter {... }public interface OnLoginFinishedListener {... }public interface LoginModel {... }}Copy the code

This concludes the simplest use of MVP. In the actual application, some adjustments should be made, such as pulling out a Base layer and encapsulating some redundant code into the corresponding Base interface or class, such as pulling out BaseView, BaseActivity, BasePresenter, BaseListener, etc. Also, try to design each SET of MVPS to implement a single function so that they can be reused, so that you don’t need to write a separate SET of MVPS for every page, and try to reuse MVPS by combination or inheritance.

Pros and Cons of MVP

Next, let’s take a look at the pros and cons of MVP. Let’s see what the advantages are:

  1. We know that the MVC pattern in the actual application of the App, the Activity and the UIViewController both act as the Controller and part of the View, the responsibilities are unclear, The Activity and UIViewController tend to get bloated. By using THE MVP mode, the Activity and UIViewController are directly divided into the View layer. The responsibilities are clear and the Activity and UIViewController are not bloated.
  2. The MVPS interact with each other through interfaces, making it easier to unit test, maintain, and extend.
  3. M and V are completely separated, reducing the coupling, and modifying V layer will not affect M layer.

Correspondingly, however, there are some disadvantages to MVC:

  1. As many interface definitions were added, the amount of code that needed to be written exploded, adding to the complexity of the project.
  2. The need to abstract the interactions between many business modules into interface definitions increases the design capabilities of developers.

MVVM

MVVM = model-view-viewModel; MVVM = model-view-viewModel; MVVM = Model-view-viewModel; MVVM was first proposed in 2005 by John Gossman, Microsoft’s WPF and Silverlight architect, and is used in Microsoft software development. John Gossman’s Introduction, “Introduction to Model/View/ViewModel Pattern for Building WPF Apps,” was relatively simple. In addition, for the convenience of those who are not familiar with English, I also found a translation.

The diagram of MVVM is as follows:

As you can see, MVVM’s diagram is very similar to MVP’s, with the biggest difference being that the View and ViewModel interact primarily through a data binding solution.

To understand MVVM, let’s first understand what a ViewModel is. What’s the difference between Model and Model?

ViewModel

Model encapsulates the business logic and data and manages the business Model. ViewModel = Model of View, which encapsulates the presentation logic and data of the View, is an abstraction of the View, including the properties and commands of the View, or the state and behavior of the View.

Let’s say we want to display information about a purchase order on the page. The view displays the order number, order status, creation time, transaction time, order amount, item name, purchase quantity, and so on. So, in the business model, the data structure for the order would typically look like this:

{
    "orderId": "107896581204552"."status": "paid"."createdTime": 1592826572000."dealTime": 1592826691000."amount": 105.28."productName": "VicTsing MM057 2.4g Wireless Portable Optical Mouse with USB Receiver"."buyQty": 1
}
Copy the code

For example, you need to convert the timestamp to “YYYY-MM-DD HH: MM: SS” format, convert the paid to “paid” format, add ¥before the amount, and add multiple times before the amount. So, the data structure corresponding to the interface would look something like this:

{
    "orderId": "107896581204552"."status": "Paid"."createdTime": "The 2020-06-22 19:49:32"."dealTime": "The 2020-06-22 19:51:31"."amount": "¥105.28"."productName": "VicTsing MM057 2.4g Wireless Portable Optical Mouse with USB Receiver"."buyQty": "X1"
}
Copy the code

That’s what we mean by view data in the ViewModel, but we’re just showing the properties of the view, which is also the state of the view. But the ViewModel encapsulates not only properties, but also commands, which are view behaviors, such as what happens when the page first loads in, what happens when you click on a button, and what happens when you click on an item in the list. These are view behaviors.

Data binding

One of the most important features of MVVM is data binding. By binding the properties of a View to a ViewModel, the two are loosely coupled, and you don’t need to write code in the ViewModel to update a View directly. The data binding system also supports input validation, which provides a standardized way to transmit validation errors to a View.

With data binding, when the data of the ViewModel changes, the View bound to it is automatically updated. Conversely, if the View changes, does the ViewModel also change? There are two types of data binding:

Unidirectional binding: After the ViewModel is bound to the View, the View automatically updates when the ViewModel changes, but not the other way around, that is, the direction of data transfer is unidirectional. (ViewModel — > View)

Bidirectional binding: After a ViewModel is bound to a View, if either View or ViewModel changes, the other will automatically update. This is called bidirectional binding. (Model < — > View)

In general, data that is only displayed in the view without editing is bound unidirectional, and data that needs editing is bound bidirectional.

As we have already seen, the data encapsulated by the ViewModel includes both the properties of the View and the commands. Therefore, data binding can be divided into property binding and command binding. For example, the content of a TextView is bound to a property, and the click event of a Button is bound to a command.

To implement data binding, the publisher-subscriber pattern is often used, but this part of the work can be complicated by developers writing their own code, so each platform provides its own internal implementation. For example, Vue and React have implemented data binding themselves. Currently, the most mainstream solution for Android is Jetpack, and the most common solution for iOS is ReactiveCocoa (RAC).

The use of MVVM

We will focus on how to implement the MVVM architecture with Jetpack. Jetpack provides a number of architecture components, including ViewModel, LiveData, DataBinding, etc. The official recommended application architecture for Android is as follows:

If this architecture corresponds to MVVM, then activities/fragments belong to the View layer, and Repository and the following models and Remote Data sources belong to the Model layer in MVVM. DataBinding is not mentioned in this architecture diagram, but we will use it. We will use a combination of DataBinding, ViewModel, and LiveData to meet the DataBinding requirements. As for the basic usage of these components, I will not explain in detail, unfamiliar to learn the next.

Let’s take the login page as an example. Our page will display four controls: the login account input box, the password input box, the login button, and the UID returned after successful login. After the user enters the login account and password and clicks the login button, the login request will be sent to the server. After the login succeeds, the UID will be returned, and the UID will be displayed on the page. The login account will be cached in the local database, and the second time the page is opened, the login account will be read from the cache and displayed directly, without further input.

LoginRepository

First, let’s talk about the Model layer. The Model layer mainly needs to realize two functions. First, the login is realized through network request, and the UID will be obtained after the login succeeds. The second is to save the login account to SQLite to realize the cache function of the login account. For simplicity, instead of writing complete code, we’ll simply simulate the implementation process in pseudo-code:

public class LoginRepository {...public String login(String userName, String password) {
      	// Call the network request
      	String uid = apiService.login(userName, password);
        // Cache userName to the local database
        cache.save("userName", userName);
      
        return uid;
    }
  	// Retrieve the login account from the cache
  	public String getUserNameFromCache(a) {
      	return cache.get("userName"); }}Copy the code

The code is simple. The login() method takes two parameters: the username and password, calls the login interface to return the UID, then cache the username, and finally returns the UID. The getUserNameFromCache() method has simpler logic and simply reads and returns from the cache.

LoginViewModel

Now, let’s talk about the ViewModel, which is the key. Let’s look at the code first:

public class LoginViewModel extends ViewModel {
    public MutableLiveData<String> userName;
    public MutableLiveData<String> password;
    public MutableLiveData<String> uid;

    private LoginRepository repository;
		
    public LoginViewModel(a) {
        repository = new LoginRepository();
        userName.postValue(repository.getUserNameFromCache());
        password = new MutableLiveData<>("");
        uid = new MutableLiveData<>("0");
    }

    public void login(a) { String userId = repository.login(userName.getValue(), password.getValue()); uid.postValue(userId); }}Copy the code

Although the amount of code is also relatively small, but there is more information in it, and listen to me.

First, we inherited the ViewModel, a component provided by Jetpack that encapsulates data from interface controllers such as activities and fragments so that it survives configuration changes. In plain English, this means separating the interface from the data, which is a key idea of MVVM. If not separated, if the device screen is rotated, then we need to do the data saving and recovery work. After separation, the data is not affected by the life cycle of these intermediate states of the interface controller.

Second, you’ll notice that the LoginViewModel has no reference to an Activity or Fragment, or to an instance of the LoginView interface as defined by MVP. That said, the ViewModel does not depend on the View layer, so it is naturally easier to test and reuse.

We then declare the variables userName, password, and UID to be of type MutableLiveData to automatically notify the interface of data changes. Therefore, in the above code, we did not add code to notify the interface to update the UI when the data changed, the mechanism behind it did so for us automatically.

Finally, the LoginViewModel is bound to the View interface in addition to the userName, password, and UID properties. In fact, there is another command binding, the login() method, which binds the click event of the button.

LoginActivity

Finally, there is the View layer. The core code for LoginActivity is simple, as follows:

public class LoginActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
				// Initialize the viewModel
        LoginViewModel viewModel = new ViewModelProvider(this).get(LoginViewModel.class);
				// Bind to the layout file
        ActivityLoginBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_login);
        binding.setLifecycleOwner(this); binding.setViewmodel(viewModel); }}Copy the code

The viewModel is bound to the layout file in just a few lines of code. Jetpack itself helps us implement the complex logic behind it, so we can implement the functionality very easily.

The layout file activity_login.xml looks like this:


      
<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="vm"
            type="com.example.mvvm.LoginViewModel" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <EditText
            android:id="@+id/userName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={vm.userName}"
            app:layout_constraintBottom_toTopOf="@+id/password"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
        <EditText
            android:id="@+id/password"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:inputType="textPassword"
            android:text="@={vm.password}"
            app:layout_constraintBottom_toTopOf="@+id/login"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/userName" />
        <Button
            android:id="@+id/login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{() -> vm.login()}"
            android:text="Login"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/password" />
        <TextView
            android:id="@+id/uid"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{vm.uid}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/login" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Copy the code

The variable tag in data defines our LoginViewModel, which I’ll name VM, and which can then be referenced in the control below.

How is the value of the Android :text property set for the two EditText and the last TextView? @={vm. UserName}, @={vm. Password}, and @{vm. If the @ sign is not followed by an equal sign, then it is only one-way binding, and only the ViewModel can notify the interface of data changes. The equal sign is two-way binding, meaning that changes in the interface are passed to the ViewModel.

Take a look at the Android :onClick property value of the Button, set to @{() -> vm.login()}, which is one way of binding the Button’s click event to the login() method of the ViewModel.

So far, the use of MVVM is explained here.

conclusion

In summary, BOTH MVP and MVVM are designed to solve the separation of interface and data, they just use different implementations. MVPS interact with each other primarily through interfaces, and the main drawback is that you need to write a lot of interfaces. MVVM, on the other hand, is implemented through data binding, which relies on specific framework tools, but significantly reduces the amount of code that developers need to write.


Scan the following QR code to follow the public account (public account name: Keegan Xiaosteel)