[This is the 15th original TECHNICAL article by ZY]

Preliminary knowledge

  1. Understand basic Android development

How far can I go after reading this article

  1. Know how to analyze an architectural pattern
  2. Master MVC, MVP, MVVM architecture definition and implementation

Prepare before reading

  1. The Clone CommonTec project, where the Architecture module is the sample code corresponding to the architecture pattern in this article

The article gives an overview of

I. What is architecture

The definition of architecture is actually different in many books and articles, and it is difficult to make a unified one. Here are two definitions: According to Wikipedia, a software architecture is a sketch of a system. Software architecture describes the abstract components that directly make up a system. The connections between the components clearly and relatively carefully describe the communication between the components. In the implementation phase, these abstract components are refined into actual components, such as concrete classes or objects. As defined in the VOCABULARY of IEEE software Engineering Standards, architecture is the basic organizational structure of a system composed of components, the relationships between components, and the relationships between components and the environment, as well as the principles guiding the design and evolution of these contents.

For more definitions, it is recommended to read Chapter 2 of Software Architecture Design: Essentials for the Transition from Programmer to Architect…

After looking at many definitions of architecture, my understanding of architecture goes something like this:

  1. Put forward in order to solve a particular problem
  2. The system as a whole is divided into modules/components/roles according to specific principles
  3. Establish communication mechanisms between modules/components/roles

To explain in detail, first of all, there must be a specific problem, no problem talking about the structure, as if it is a castle in the air, no practical value, and corresponding to different problems, there will be different solutions. Secondly, the division of modules should be based on specific principles. Without arbitrary division of principles, there is no way to evaluate the quality of an architecture. Finally, the communication mechanism between modules makes the system a whole.

Finally, an architectural pattern is actually more of an idea, a rule, often an architectural pattern may have different ways of implementation, and the implementation of the way, only appropriate or not, there is no right or wrong.

How to analyze an architectural pattern

The definition of architecture described above will be followed by an analysis of architectural patterns in the following three areas.

  1. What problem does the architecture solve By knowing the problem that the architectural pattern is trying to solve, we can look at it and think about whether the solution is appropriate or not.
  2. The most important part of architecture is the division of roles/modules. Understanding the division of roles in an architectural pattern will help you understand its structure.
  3. How roles communicate The communication between roles is also important. The same role division, using different communication methods, often constitute different architectural patterns. Communication between roles can be understood as the flow of data. In Android development, data in communication can be understood as two kinds of data structures, i.e., Javabeans used in communication such as network requests, local storage, etc., and events, which are actions generated by controls, including touches, clicks, slides, and so on. We also focus on these two kinds of data in the communication process.

What are the common architectural patterns

For us Android developers, the common architectural patterns are basically MVC, MVP, and MVVM, which are also common patterns for developing GUI applications. In addition, there are layered modes, client-server mode (CS mode), master-slave mode, pipe filter mode, event bus mode, and so on. This article also specifically analyzes MVC, MVP, MVVM these three architectural patterns.

4. How is the App developed before the use of architecture

After we understand the definition of architecture, we may wonder why we use these architectural patterns at all. Before we understand these patterns, it is the same development. Similar to the design pattern, the purpose of the architectural pattern is not to let the application software development, but to make the structure more clear, more clear division of labor, expansion more convenient and so on.

We can take a look at how we developed without using architectural patterns. For example, we have three controls on the interface: EditText, TextView, Button. The function to achieve is also relatively simple:

  1. EditText accepts user input
  2. Process user input data
  3. The data is processed and output to TextView
  4. Click Button to clear the user’s input

The interface is as follows:

Let’s take a look at how we develop without using architectural patterns, which is how we typically develop:

  1. Start with the XML design interface
<?xml version="1.0" encoding="utf-8"? >
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/titleText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Normal" />

    <EditText
        android:id="@+id/edit"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:textColor="@android:color/darker_gray" />

    <TextView
        android:id="@+id/msgText"
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:layout_marginTop="10dp"
        android:text="default msg"
        android:textColor="@android:color/darker_gray" />

    <TextView
        android:id="@+id/clearText"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_marginTop="10dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="clear"
        android:textColor="@android:color/white" />
</LinearLayout>
Copy the code
  1. Get a View in an Activity or Fragment and listen for events
  2. Data is obtained through the View event and processed
  3. Set the processed data to the View code as follows:
class NormalFragment : Fragment() { companion object { fun newInstance(): Fragment { return NormalFragment() } } private val handler: Handler = Handler() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup? , savedInstanceState: Bundle?) : View? { return inflater.inflate(R.layout.architecture, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) titleText.text = "NORMAL" edit.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) { handleData(s.toString()) } override fun beforeTextChanged(s: CharSequence? , start: Int, count: Int, after: Int) { } override fun onTextChanged(s: CharSequence? , start: Int, before: Int, count: Int) {}}) clearText. SetOnClickListener {edit. The setText (" ")}} / / data processing, real cases may be a network request, the disk access, Private fun handleData String) { if (TextUtils.isEmpty(data)) { msgText.text = "default msg" return } msgText.text = "handle data ..." Handler. RemoveCallbacksAndMessages (null) / / delay to simulate network or disk operation handler. PostDelayed ({msgText. Text = "handled data: $data" }, 3000) } }Copy the code

Disadvantages of the default development method: If we look at the above code, one obvious feature is that the processing logic is concentrated in the Activity/Fragment, whether it is to operate on the View, or to handle the data. The problem is that there is too much logic in the Activity/Fragment, and the subsequent extension is all that matters. In addition, the unclear division of responsibilities also brings difficulties to follow-up maintenance. In that case, let’s take a look at what the transformation looks like using architectural patterns.

5. MVC architecture

5.1 Mode Introduction

In fact, there are some differences in the implementation of MVC architecture in different frameworks, which also shows that architecture is an idea. We’ll use a more mainstream implementation here.

1. What problem is it solving

As we can see, the problem with developing without an architecture is that the Activity/Fragment logic is bloated and not good for extension. So the problem that MVC solves is: control logic, data processing logic and interface interaction coupling. As an aside, we as programmers write code not only to fulfill requirements, but also to make it easy to read and expand. This point, often can also reflect the power, not to say that the use of a variety of strange technology is a great god. In Java Swing, the interface/control Settings are also implemented using Java code. If you do not use the architecture, the final result is that the control logic, data processing and page presentation code are all in one class. As you can imagine, this code is hard to maintain.

2. How to divide roles

To solve the above problems, the MVC architecture divides the processing of logic, data and interface into three parts: Model – View – Controller. The functions of each part are as follows:

  • Model The Model that is responsible for loading and storing data.
  • View, responsible for the display of the interface.
  • Controller Controller, responsible for logical control.
3. How to communicate (the flow of data)

Let’s see how they communicate with each other. Before introducing communication, let’s explain what data is in communication. In fact, in Android development, communication data can be understood as two kinds, one is data structures, that is, network requests, local storage and other communication javabeans used, and the other is events, that is, controls generated actions, including touch, click, slide and so on. We also focus on these two kinds of data in the communication process. In the MVC architecture, the View generates events, which are notified to the Controller, which performs a series of logical processing, and then notified to the Model to update the data, which, after the Model updates the data, informs the View of the data structure to update the interface. This is the data flow of a full MVC.

5.2 Concrete implementation in Android

Now that you understand the MVC pattern, let’s look at its implementation. In fact, in Android development, the default can be understood as MVC structure, put the View in XML and decouple the Java code, and then the Activity/Fragment acts as Controller for logical control, However, Android itself does not divide the Model, so we often use the Activity/Fragment as Model and Controller. And often the View operation in XML is also in the Activity/Fragment, so sometimes the Activity/Fragment will also act as some View. So what we’re going to do in the implementation, we’re going to have the Fragment acting as the View, and we’re going to have the Model and the Controller logic. Let’s define three interfaces as follows:

// The data model interface defines the operations of the data model
interface IModel {
    fun setView(view: IView)
    // The data model processes the input data
    fun handleData(data: String)
    // Clear the data
    fun clearData(a)
}

// The view interface defines the actions of the view
interface IView {
    fun setController(controller: IController)
    // The status of data processing
    fun dataHanding(a)
    // Update the interface after data processing
    fun onDataHandled(data: String)
}

// The controller interface defines the logic of the controller
interface IController {
    fun setModel(model: IModel)
    // EditText Notifies the controller of data changes
    fun onDataChanged(data: String)
    // Clear the button click event
    fun clearData(a)
}
Copy the code

The above three interfaces define the operations of Model, View, and Controller respectively. One thing to note is that according to the MVC communication process, the View needs to hold the Controller, the Controller needs to hold the Model, and the Model needs to hold the View, so you need to expose the corresponding interface. Let’s look at the implementation:

  • The realization of the Model

To handle the data in the Model, add the “HANDLED data:” prefix and add a delay of 3 seconds.

class HandleModel : IModel {
    private var view: IView? = null
    private val handler: Handler = Handler(Looper.getMainLooper())

    override fun setView(view: IView) {
        this.view = view
    }

    // After the data is received, it is processed. The delay of 3 seconds is set here to simulate the operation of network request processing data
    override fun handleData(data: String) {
        if (TextUtils.isEmpty(data)) {
            return} view? .dataHanding() handler.removeCallbacksAndMessages(null)
        // Delay to simulate network or disk operations
        handler.postDelayed({
            // When data processing is complete, notify the View to update the interfaceview? .onDataHandled("handled data: $data")},3000)}// Delete data directly after receiving an event to delete data
    override fun clearData(a) {
        handler.removeCallbacksAndMessages(null)
        // After the data is cleared, notify the View to update the interfaceview? .onDataHandled("")}}Copy the code
  • The realization of the Controller

The implementation of the Controller is relatively simple, forwarding operations directly to the Model, and in fact there is a lot of business logic to handle for complex business scenarios.

class HandleController : IController {
    private var model: IModel? = null

    override fun onDataChanged(data: String){ model? .handleData(data)}override fun clearData(a){ model? .clearData() }override fun setModel(model: IModel){}}Copy the code
  • The implementation of the View

Here the Fragment acts as a View and is responsible for passing events from the View to the Controller and receiving data from the Model for interface updates.

class MVCFragment : Fragment(), IView {

    companion object {
        fun newInstance(a): Fragment {
            return MVCFragment()
        }
    }

    private val model: IModel = HandleModel()
    private var controller: IController = HandleController()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup? , savedInstanceState:Bundle?).: View? {
        return inflater.inflate(R.layout.architecture, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?). {
        super.onViewCreated(view, savedInstanceState)
        setController(controller)
        model.setView(this)

        titleText.text = "MVC"
        edit.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable?). {
                // Notify the Controller that the input data has changedcontroller? .onDataChanged(s.toString()) }override fun beforeTextChanged(s: CharSequence? , start:Int, count: Int, after: Int){}override fun onTextChanged(s: CharSequence? , start:Int, before: Int, count: Int) {
            }
        })
        clearText.setOnClickListener {
            // Notify the Controller to clear the data eventcontroller? .clearData() } }// Update the interface when the Model data changes
    override fun onDataHandled(data: String) {
        if (TextUtils.isEmpty(data)) {
            edit.setText("")
            msgText.text = "default msg"
        } else {
            msgText.text = data}}// Update the interface when the Model data changes
    override fun dataHanding(a) {
        msgText.text = "handle data ..."
    }

    override fun setController(controller: IController) {
        this.controller = controller
    }
}
Copy the code

So we’ve implemented a simple MVC structure.

5.3 Advantages and disadvantages of the MVC architectural pattern

Advantages:

  1. Clear structure and clear division of responsibilities
  2. To reduce the coupling
  3. Facilitates component reuse

Disadvantages:

  1. In fact, the above example is an optimized MVC structure. Generally speaking, the Activity/Fragment will assume the roles of View and Controller, which will lead to more code in the Activity/Fragment
  2. The Model directly manipulates the View. Changes to the View result in changes to both the Controller and the Model
  3. Increases the complexity of the code structure

MVP Architecture

6.1 Mode Introduction

1. What problem is it solving

MVP solves the same problem as MVC: control logic, data processing logic and interface interaction coupling, while decoupling View and Model in MVC.

2. How to divide roles

The MVP architecture divides logic, data, and interface processing into three parts: Model, View, and Presenter. The functions of each part are as follows:

  • Model The Model that is responsible for loading and storing data.
  • View, responsible for the display of the interface.
  • Presenter controller, responsible for logic control.
3. How to communicate (the flow of data)

As you can see, the roles in MVP are basically similar to MVC, so what are the differences? The difference is in the communication of the roles.

The biggest difference between MVP and MVC is that View and Model are not held by each other, and both are transferred by Presenter. The View generates events and informs the Presenter. After logical processing, the Presenter informs the Model to update the data. After the Model updates the data, the data structure is notified to the Presenter, and the Presenter informs the View to update the interface.

This is the data flow for a complete MVP.

6.2 Implementation in Android

With MVP understood, let’s take a look at its implementation. First we define three interfaces:

// The model interface defines the operation of the data model
interface IModel {
    fun setPresenter(presenter: IPresenter)
    // Comb the data
    fun handleData(data: String)
    // Clear the data
    fun clearData(a)
}

// View interface, which defines the actions of the view
interface IView {
    fun setPresenter(presenter: IPresenter)
    // View in data processing
    fun loading(a)
    // Data display
    fun showData(data: String)
}

// The controller defines the logical operations
interface IPresenter {
    fun setView(view: IView)
    fun setModel(model: IModel)
    // Model completes data processing to notify Presenter
    fun dataHandled(data: String)
    // Model clears data to notify Presenter
    fun dataCleared(a)
    // The EditText in the View notifishes the Presenter when the text changes
    fun onTextChanged(text: String)
    // Click the Button in the View to notify the event Presenter
    fun onClearBtnClicked(a)
}
Copy the code

It defines View, Model, and Presenter interfaces, where View and Model hold Presenter, and Presenter hold View and Model. Next, look at the implementation of the interface:

  • The realization of the Model
class HandleModel : IModel {
    private var presenter: IPresenter? = null
    private var handler = Handler(Looper.getMainLooper())

    override fun handleData(data: String) {
        if (TextUtils.isEmpty(data)) {
            return
        }
        handler.removeCallbacksAndMessages(null)
        // Delay to simulate network or disk operations
        handler.postDelayed({
            // Presenter is notified when data processing is completepresenter? .dataHandled("handled data: $data")},3000)}override fun clearData(a) {
        handler.removeCallbacksAndMessages(null)
        // Notify Presenter of data cleanuppresenter? .dataCleared() }override fun setPresenter(presenter: IPresenter) {
        this.presenter = presenter
    }

}
Copy the code

The Model implementation is basically the same as the previous MVC implementation, except that in MVC, the Model directly operates the View to present the View, whereas in MVP, the Model notifishes the Presenter to relay.

  • The implementation of the View

Once again, the Fragment acts as the View, passing events from the View to the Presenter and updating the interface with data received from the Presenter.

class MVPFragment : Fragment(), IView {

    companion object {
        fun newInstance(a): Fragment {
            val presenter = Presenter()
            val fragment = MVPFragment()
            val model = HandleModel()
            fragment.setPresenter(presenter)
            model.setPresenter(presenter)
            presenter.setModel(model)
            presenter.setView(fragment)
            return fragment
        }
    }

    var mpresenter: IPresenter? = null

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup? , savedInstanceState:Bundle?).: View? {
        return inflater.inflate(R.layout.architecture, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?). {
        super.onViewCreated(view, savedInstanceState)
        titleText.text = "MVP"

        edit.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable?). {
                // Pass the text modification event to the Presentermpresenter? .onTextChanged(s.toString()) }override fun beforeTextChanged(s: CharSequence? , start:Int, count: Int, after: Int){}override fun onTextChanged(s: CharSequence? , start:Int, before: Int, count: Int) {
            }
        })
        clearText.setOnClickListener {
            // Pass the button click event to Presentermpresenter? .onClearBtnClicked() } }override fun setPresenter(presenter: IPresenter) {
        this.mpresenter = presenter
    }

    // Display the view in the data processing
    override fun loading(a) {
        msgText.text = "handling data ..."
    }

    // Display the processed data
    override fun showData(data: String) {
        msgText.text = data}}Copy the code
  • The realization of the Presenter

The implementation of the Presenter is relatively simple, and there is not much business logic to it. In real life, the business logic will be handled here.

class Presenter : IPresenter {
    private var model: IModel? = null
    private var view: IView? = null

    override fun setModel(model: IModel) {
        this.model = model
    }

    override fun setView(view: IView) {
        this.view = view
    }

    override fun dataHandled(data: String){ view? .showData(data)}override fun dataCleared(a){ view? .showData("")}override fun onTextChanged(text: String){ view? .loading() model? .handleData(text) }override fun onClearBtnClicked(a){ model? .clearData() } }Copy the code

6.3 Pros and cons of the MVP architectural pattern

Advantages:

  1. Clear structure and clear division of responsibilities
  2. Fully decoupled between modules
  3. Facilitates reuse of components

Disadvantages:

  1. A large number of interfaces are introduced, causing the number of project files to explode
  2. Increases the structural complexity of your code

7. MVVM architecture

7.1 Mode Introduction

1. What problem is it solving

MVVM solves the same problem as MVC and MVP: control logic, data processing logic and interface interaction, and can decouples View and Model in MVC, and can decouples Presenter and View in MVP.

2. How to divide roles

MVVM architecture, logic, data, interface processing is divided into three parts, Model (Model)- (View)- (ViewModel). The functions of each part are as follows:

  • Model The Model that is responsible for loading and storing data.
  • View, responsible for the display of the interface.
  • ViewModel controller, which is responsible for logical control.
3. How to communicate (the flow of data)

As we can see, the division of roles in MVP is basically similar to MVC and MVP, but the difference is also in the communication of roles.

As we mentioned above, in MVP, the View and Model are not held by each other, and both are mediated by a Presenter. This decouples the View from the Model.

In MVVM, decoupling is more complete and viewModels do not hold views. Changes in the ViewModel are automatically fed back to the View for interface update, and events in the View are also automatically fed back to the ViewModel.

To achieve this, of course, you need to use some tools, the most common of which is databinding. In MVVM, the flow of data looks like this: The View generates events that automatically notify the ViewMode. After logical processing in the ViewModel, the Model is notified to update the data. After the Model updates the data, the data structure is notified to the ViewModel, and the ViewModel automatically notifishes the View to update the interface. This is the data flow for a full MVVM.

7.2 Implementation in Android

The MVVM implementation is a little more complicated, so let’s first look at the interface definition:

// The ViewModel interface defines the logical operations
interface IViewModel {
    fun setModel(model: IModel)
    fun handleText(text: String?).
    fun clearData(a)
    fun dataHandled(data: String?).
    fun dataCleared(a)
}

// Model interface, which defines data operations
interface IModel {
    fun setViewModel(viewModel: IViewModel)
    fun handleData(data: String?).
    fun clearData(a)
}
Copy the code

The interface in MVVM only defines the ViewModel and the Model. There is no View interface because the View is via databind and ViewModel. Let’s look at the implementation:

  • The Model implemented

The implementation of the Model is basically the same as above, which is to process the data and notify the ViewModel when the processing is complete.

class HandleModel : IModel {
    private var viewModel: IViewModel? = null
    private var handler = Handler(Looper.getMainLooper())

    override fun handleData(data: String?). {
        if (TextUtils.isEmpty(data)) {
            return
        }
        handler.removeCallbacksAndMessages(null)
        // Delay to simulate network or disk operations
        handler.postDelayed({
            // Notifies ViewModel of data processing completionviewModel? .dataHandled("handled data: $data")},3000)}override fun clearData(a) {
        handler.removeCallbacksAndMessages(null)
        // Notifies ViewModel when data cleaning is completeviewModel? .dataCleared() }override fun setViewModel(viewModel: IViewModel) {
        this.viewModel = viewModel
    }
}
Copy the code
  • The ViewModel implementation

The ViewModel implementation is a little different. We use Databind to bind the ViewModel to the View. It defines two variables, inputText, which is bidirectional bound to EditText, and handledText, which is bidirectional bound to TextView. The inputText registered listener is notified when the data entered in the EditText is changed, and changes in the handledText value are automatically displayed on the interface.

class ViewModel : IViewModel {
    private var model: IModel? = null
    // inputText and handledText will automatically notify the View update interface when the data bound to the View is updated
    var inputText: MutableLiveData<String> = MutableLiveData()
    var handledText: MutableLiveData<String> = MutableLiveData()

    init {
        // Register the data listener and notify the Model to process the data when it changes
        inputText.observeForever {
            handleText(it)
        }
        handledText.value = "default msg"
    }

    override fun handleText(text: String?). {
        if (TextUtils.isEmpty(text)) {
            handledText.value = "default msg"
            return
        }
        handledText.value = "handle data ..."model? .handleData(text) }// Clear the click event binding of the button
    override fun clearData(a){ model? .clearData() }override fun setModel(model: IModel) {
        this.model = model
        model.setViewModel(this)}// Model data processing is complete, set the handledText value, automatically updated to the interface
    override fun dataHandled(data: String?). {
        handledText.value = data
    }

    // Set the inputText value and update it to the interface automatically
    override fun dataCleared(a) {
        inputText.value = ""}}Copy the code
  • The View to achieve

Take a look at the data binding in the View.

class MVVMFragment : Fragment() {
    companion object {
        fun newInstance(a): Fragment {
            return MVVMFragment()
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup? , savedInstanceState:Bundle?).: View? {
        // Use databind for data binding
        var binding: ArchitectureBindingBinding = DataBindingUtil.inflate(inflater, R.layout.architecture_binding, container, false)
        binding.lifecycleOwner = this
        val viewModel = ViewModel()
        viewModel.setModel(HandleModel())
        binding.viewmodel = viewModel
        return binding.root
    }
}
Copy the code
<?xml version="1.0" encoding="utf-8"? >
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <! -- Define the data bound to the View -->
    <data>
        <variable
            name="viewmodel"
            type="com.zy.architecture.mvvm.ViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="10dp"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/titleText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="MVVM" />

        <! -- Bind inputText to EditText-->
        <EditText
            android:id="@+id/edit"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="@={viewmodel.inputText}" 
            android:textColor="@android:color/darker_gray" />

        <! -- Bind handledText to TextView-->
        <TextView
            android:id="@+id/msgText"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:layout_marginTop="10dp"
            android:text="@{viewmodel.handledText}"
            android:textColor="@android:color/darker_gray" />

        <! -- Bind empty data click event to TextView-->
        <TextView
            android:id="@+id/clearText"
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:layout_marginTop="10dp"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:onClick="@{() -> viewmodel.clearData()}"
            android:text="clear"
            android:textColor="@android:color/white" />
    </LinearLayout>
</layout>
Copy the code

When the EditText changes, the inputText listener is triggered, and the ViewModel passes the message to the Model for processing. When the Model completes processing, Tells the ViewModel to update the value of handledText, which is automatically updated to the interface. When the clear button is clicked, the bound click function is automatically called and the ViewModel is notified of the clear event. The ViewModel passes the message to the Model for data clearing. After the Model completes data processing, the ViewModel is notified to update the interface.

7.3 Advantages and disadvantages of the MVVM architectural pattern

Advantages:

  1. Clear structure and clear division of responsibilities
  2. Fully decoupled between modules
  3. On top of MVP, MVVM decouples View and ViewModel

Disadvantages:

  1. Debug is difficult. Because View and ViewModel are decoupled, it is difficult to see View event passing at a glance during Debug
  2. Code complexity increases

8. Application of architectural patterns

In the previous article, we introduced the MVC, MVP, and MVVM architectural patterns and their simple implementations. Here we go back and think, when should we use architectural patterns? Architectural patterns can make code modules clear, responsibilities clear, and easy to scale, with the side effect of introducing a large number of interfaces, resulting in a proliferation of code files. As we said at the outset, architectural patterns are designed to solve specific problems, and if that particular problem is not a problem at this stage, or a major problem, then we can leave them out of the equation. For example, if a function is very simple, the amount of code is small, and there is no subsequent need for extension, then we can directly use the traditional way of development, quickly and clearly, there is no need to build for the sake of architecture. It’s also a good idea to slowly refactor code that doesn’t consider architectural patterns at the beginning.

The bottom line is: although the structure is good, don’t drink too much

conclusion

The resources

www.infoq.cn/article/an-… Zh.wikipedia.org/wiki/ software architecture www.jianshu.com/p/4ce4dcb43… Software Architecture Design — Essentials for Transition from Programmer to Architect architecture Practice Architecture Beauty