Overview When it comes to Android MVVM, I believe everyone will think of the DataBinding framework launched by Google in 2015. However, the concepts of the two are different and cannot be confused. MVVM is an architectural pattern, while DataBinding is a framework for implementing data and UI binding, and is a tool for building the MVVM pattern. I have read many blogs about Android MVVM before, but most of them mentioned the basic usage of DataBinding. Few articles explain in detail how to use DataBinding to build an MVVM application framework in Android. What are the responsibilities of each layer of View, ViewModel, and Model? How are they connected, how is the division of labor, and how should the code be designed? This is the original intention of writing this article. Next, let’s take a look at what MVVM is, and then design the entire MVVM framework step by step. MVC, MVP, MVVM First, let's take a look at the common patterns in Android development. MVC View: XML layout file. Model: Entity model (data acquisition, storage, data state changes). Controller: corresponds to Activity, handles data, business and UI. From the above structure, the design of Android itself is still in line with the MVC architecture, but the XML view function of Android as a pure View is too weak. We can only write a lot of View logic in the Activity, so the Activity plays the role of both View and Controller, which directly leads to a code explosion in the Activity. I believe that most Android developers have encountered an Activity with thousands of lines of code! Therefore, it is more appropriate to say that this MVC structure is ultimately just a Model-View (Activity: View & Controller) structure. MVP View: corresponds to Activity and XML, responsible for drawing the View and interacting with the user. Model: Still a physical model. Presenter: Responsible for completing the interaction and business logic between View and Model. As we said before, Activity plays the role of both View and Controller. MVP can solve this problem very well. Its core concept is to decouple Presenter from the real View layer through an abstract View interface (not the real View layer). Presenter holds the View interface and operates on it instead of directly operating the View layer. In this way, the view operation and business logic can be decoupled, making Activity the real View layer. But MVP also has some disadvantages:
MVVM View: corresponds to Activity and XML, responsible for drawing the View and interacting with the user. Model: Entity model. ViewModel: Responsible for completing the interaction between View and Model and responsible for business logic. The goals and ideas of MVVM are similar to those of MVP. It uses new features such as data binding, dependency property, command, and routed event to create a more flexible and efficient architecture. Data-driven In the conventional development mode, when the UI needs to be updated due to data changes, you need to obtain the reference of the UI control first, and then update the UI. Obtaining user input and operations also requires the reference of the UI control. In MVVM, these are all done automatically through data drive. The UI will be automatically updated after the data changes, and the changes in the UI can also be automatically fed back to the data layer, and data becomes the dominant factor. In this way, the MVVM layer only needs to care about the data in the business logic processing, and does not need to deal with the UI directly, which is much simpler and more convenient in the business processing process. Low coupling In the MVVM pattern, data is independent of the UI. The data and business logic are in an independent ViewModel. The ViewModel only needs to focus on the data and business logic, and does not need to deal with the UI or controls. The UI decides how to handle the data. The ViewModel does not involve anything related to the UI, nor does it hold references to UI controls. Even if the control changes (for example, TextView is replaced by EditText), the ViewModel hardly needs to change any code. It perfectly decouples the View layer and ViewModel, solving the pain points of MVP mentioned above. Update UI In MVVM, when the data changes, we can directly modify the ViewModel data in the working thread (if the data is thread-safe) without having to consider switching to the main thread to update the UI. The relevant framework will do these things for us. Teamwork The division of labor in MVVM is very obvious, because View and ViewModel are loosely coupled: one handles business and data, and the other handles UI. Therefore, it is more efficient to have two people do the work, one doing UI (XML and Activity) and the other writing ViewModel. Reusability A ViewModel can be reused in multiple Views. The same data can be provided to different UIs for display. For frequent UI changes in version iterations, just update or add a set of Views. If you want to do A/B Testing on the UI, MVVM is your best choice. Unit Testing Some students may feel overwhelmed when they see unit testing. Yes, how can you do unit testing on such a messy code? If you avoid it by saying that the code is too bad to write unit tests, that's really bad news. At this time, you need MVVM to save you. As we said before, the ViewModel layer is responsible for data processing and business logic, while the View layer focuses on the UI. The two are completely independent of each other. Whether it is the unit test of the UI or the unit test of the business logic, they are all low-coupling. In MVVM, data is directly bound to UI controls (some data can directly reflect the content on the UI), so we can indirectly do some tests on the Android UI by modifying the bound data source. Through the above brief description and comparison of modes, we can find that the advantages of MVVM are still very obvious. Although there may be very few people who are actually using MVVM in Android development, it is worth some discussion and research. How to build an MVVM application framework How to divide the work To build an MVVM framework, we must first understand the division of labor of each module. Next, we will explain the responsibilities of View, ViewModel, and Model. View The View layer does work related to the UI. We only write View layer code in XML, Activity, and Fragment. The View layer does not do anything related to the business, that is, we do not write business logic and business data related code in Activity. Updates to the UI are implemented through data binding, and we try to do it in the ViewModel (just update the bound data source). What the Activity has to do is to initialize some controls (such as the color of the controls, add the dividing line of the RecyclerView), the View layer can provide an interface for updating the UI (but we prefer that all UI elements are driven by data to change the UI), and the View layer can handle events (but we prefer that UI events are bound through Command). Simply put: the View layer does not do any business logic, does not involve operating data, does not process data, and the UI and data are strictly separated. ViewModel The ViewModel layer does exactly the opposite of the View layer. The ViewModel only does things related to business logic and business data, and does not do anything related to the UI. The ViewModel layer does not hold references to any controls, and does not update the UI through references to UI controls in the ViewModel. The ViewModel is focused on business logic processing, and all it does is operate on data (these data are bound to the corresponding controls and will automatically change the UI). At the same time, the DataBinding framework already supports two-way binding, allowing us to obtain data fed back to the ViewModel layer by the View layer through two-way binding, and operate on these data. Regarding the processing of UI control events, we also hope to bind these event processing to the controls and unify the processing of these events. For this reason, we encapsulate some common events through BindingAdapter, encapsulating each event into a Command. For each event, we only need to use a ReplyCommand to process it. The ReplyCommand will bring you the data you may need, which allows us to only care about processing data when processing events in the ViewModel layer. For details, see the Command section of the MVVM Light Toolkit User Guide. Let me emphasize again: ViewModel does not do anything related to UI. Model The biggest feature of the Model layer is that it is given the responsibility of data acquisition, which is completely different from the usual Model layer that only defines the behavior of entity objects. In the example, data acquisition, storage, and data state changes are all tasks of the Model layer. The Model includes entity models (Beans), Retrofit's Service, network data acquisition interfaces, local storage (add, delete, modify, and query) interfaces, data change monitoring, etc. The Model provides a data acquisition interface for ViewModel to call, and after data conversion and operation, it is finally mapped and bound to the attributes of a UI element in the View layer. How to collaborate Regarding collaboration, let’s first look at the following picture: The above diagram reflects the connection between the modules and the direction of data flow in the MVVM framework. Let's look at each module one by one. Then our focus is on the following three collaborations.
Collaboration between ViewModel and View In Figure 2, the ViewModel and View are connected by binding. Binding is divided into two types: data binding and command binding. Data binding DataBinding is already provided. Simply define some ObservableFields to bind data and controls together (such as the text property of TextView). However, the DataBinding framework is not comprehensive enough. For example, how to bind a URL to an ImageView so that the ImageView can automatically load the image specified by the URL? How to bind the data source and layout template to a ListView so that the ListView does not need to write Adapter and ViewHolder related things? These require us to do some work and simple encapsulation. MVVM Light Toolkit has done part of the work for us. For details, please refer to the MVVM Light Toolkit User Guide. The same is true for event binding. MVVM Light Toolkit has done a simple encapsulation. For each event, we just need to use a ReplyCommand to handle it. ReplyCommand will bring you the data that may be needed, so that when we handle the event, we only need to care about processing the data. From the ViewModel module in Figure 1, we can see that the ViewModel class generally contains the following five parts:
Let's take a look at the sample code first, and then explain what the five parts are used for one by one:
Context What is Context used for? Why does each ViewModel need to hold a reference to Context? ViewModel does not handle UI-related matters, does not operate controls, and does not update UI, so why do we need Context? There are two main reasons:
Of course, in addition to this, calling tool classes and helper classes sometimes requires Context as a parameter, which is also one of the reasons. Model (Data Source) What is Model? Actually, it is the data source, which can be simply understood as the Bean we converted from JSON. ViewModel may need to copy and operate a lot of Model data to map data to UI, use Model fields to generate corresponding ObservableField and then bind to UI (we will not directly use Model data for binding display). Here, it is necessary to keep the original Model reference in a ViewModel, which is very useful for us, because some user operations and inputs may require us to change the data source, we may need to pass a Bean to the detail page after clicking on the list page, and we may need to submit this Model as a form to the server. All of these require our ViewModel to hold the corresponding Model (data source). Data Field Data Field is the ObservableField field that needs to be bound to the control. This is a must for ViewModel, and there is nothing much to say about it. But here is a suggestion: These fields can be slightly classified and packaged. For example, some fields may be bound to some Style properties of the control (such as length, color, size). For such fields for View Style, you can declare a ViewStyle class to package them, so that the entire code logic will be clearer. Otherwise, the ViewModel may be flooded with fields, which is difficult to manage and difficult to read. For some other fields, such as title, imageUrl, name, which belong to the data source type, these fields are also called data fields, which are closely related to business data and logic. These fields can be put together. Command (command binding) Command (command binding) is simply the processing of events (pull-down refresh, load more, click, slide, etc.). We previously handled events by getting the reference of the UI control and then setting the Listener, which is actually the Command. However, considering that it is not beautiful to write various Listeners in a ViewModel, it may be necessary to implement multiple methods to implement a Listener, but we may only want to implement one of the useful methods. More importantly, implementing a Listener may require writing some UI logic to finally get what we want. For example, if you want to listen to the ListView sliding to the bottom and trigger the load more event, you need to write an OnScrollListener in the ViewModel, and then do calculations in the onScroll method inside to calculate when the ListView slides to the bottom. In fact, the work of the ViewModel is not to handle these events. It should focus on business logic and data processing. If there is something that does not require you to calculate whether it has slid to the bottom, but automatically triggers a Command at the bottom of the slide, and returns the total number of items in the current list to you, it would be convenient for you to calculate which page of data should be requested from the server through page=itemCount/LIMIT+1. MVVM Light Toolkit helps you achieve this:
Then just bind it in the XML layout file through bind:onLoadMoreCommand.
For more information, please check the MVVM Light Toolkit User Guide, which explains the use of Command in more detail. Of course, Command is not necessary. You can write Listener in ViewModel according to your own habits and preferences, but using Command can make ViewModel more concise and easy to read. You can also define more Commands with other functions yourself, then the event handling of ViewModel will be handled by the managed ReplyCommand, so that the code will look more beautiful and clear. Command is just an encapsulation of the UI layer that isolates the UI event. When the event is triggered, the data that the ViewModel layer may need is passed to the ViewModel layer, and the event processing is unified. Whether to use it depends on your personal preference. Child ViewModel The concept of sub-ViewModel is to nest other ViewModels in ViewModel. This scenario is still very common. For example, if you have two Fragments in an Activity, and the ViewModel is divided by business, the two Fragments do different business, so they are naturally handled by two ViewModels. At this time, the ViewModel corresponding to the Activity may contain the ViewModels of the two Fragments respectively. This is a nested sub-ViewModel. Another one is for AdapterView, such as ListView RecyclerView, ViewPager, etc.
Each of their Items actually corresponds to a ViewModel, and then the current ViewModel holds a reference through ObservableList (as shown in the above code). This is also a very common nested sub-ViewModel. We actually also recommend that if a page business is very complex, do not write all the logic in one ViewModel. You can divide the page into businesses, put different businesses into different ViewModels, and then integrate them into a total ViewModel. This will make our code business clear, concise and convenient for future generations to maintain. In general, there is only a binding relationship between ViewModel and View. The properties and event handling required by the View layer are bound in XML. The ViewModel layer will not operate the UI, but only processes data according to business requirements. These data are automatically mapped to the properties of the View layer controls. As for which modules and fields are included in the ViewModel class, this needs to be weighed by the developer himself. We recommend that ViewModel should not introduce too many member variables. It is best to have only the five types of member variables mentioned above (context, model, etc.). If other types of variables can be avoided, try not to introduce them. Too many member variables will greatly damage the entire code structure. The subsequent maintainers must always pay attention to when the member variables are initialized, cleared, assigned or changed. A detail that is not careful may cause potential bugs. Code with too many unclearly defined member variables and no comments is difficult to maintain. In addition, we will bind the properties and events of UI controls through XML (such as bind: text=@{...}). If a business logic needs to pop up a Dialog, but you don't want to do the pop-up window in the ViewModel (ViewModel does not want to do UI-related things) or change the color of the icon on the ActionBar, change whether the ActionBar button is clickable, these are not written in XML (all initialized with Java code), how to bind the properties of these controls? Let's take a look at the code first:
Simply put, you can monitor any ObservableField and make corresponding UI changes based on data changes. The business layer ViewModel only needs to process data according to the business and use data to drive the UI. Collaboration between ViewModel and Model From Figure 1, ViewModel obtains network data (the same as the database) by passing parameters to the Model layer, and then maps part of the Model's data to some fields (ObservableField) of ViewModel, and retains the reference of this Model in ViewModel. Let's take a look at the general code of this block (the code involves simple RxJava, if you don't understand it, you can refer to the introduction):
Note 1: We recommend using MVVM and RxJava together. Although both have the concept of observer mode, RxJava is not used for monitoring View, but more for the conversion and processing of business data flow. The DataBinding framework is actually dedicated to the dynamic binding of View-ViewModel. It allows our ViewModel to only focus on data, and the powerful data flow conversion function provided by RxJava can be used to process various data in ViewModel, which is very useful. At the same time, the chain programming combined with Lambda expressions makes the ViewModel code very concise and easy to read and understand. Note 2: Because the Model layer in this article's sample only involves the acquisition of network data and does not involve other businesses such as database, storage, and data status changes, the source code in this article does not extract the Model layer separately. We recommend that the Model layer be extracted separately and placed in a class, and then an interface for external data acquisition and storage is provided in an interface-oriented programming manner. Collaboration between ViewModel and ViewModel In Figure 1, we can see that two ViewModels are connected by a dotted line with Messenger written in the middle. Messenger can be understood as a global message channel. The main purpose of introducing Messenger is to realize the communication between ViewModel and ViewModel. Although it can also be used for communication between View and ViewModel, it is not recommended. ViewModel is mainly used to handle business and data. Each ViewModel has corresponding business responsibilities. However, in the case of complex business, there may be cross-business. At this time, ViewModel and ViewModel need to exchange data and communicate. At this time, a global message channel is very important. For detailed usage of Messenger, please refer to the Messenger section of the MVVM Light Toolkit User Guide. Here is a simple example for reference: the scenario is like this, your MainActivity corresponds to a MainViewModel, which contains a Fragment in addition to its own content. The business processing of this Fragment corresponds to a FragmentViewModel, and the FragmentViewModel requests the server and obtains data. It happens that the MainViewModel also needs this data, and we cannot request the data again in the MainViewModel, which is not reasonable. At this time, we need to pass the data to the MainViewModel, so how should we pass it if there is no reference or callback between them? Then it can only be done through the global message channel Messenger. After receiving the message, FragmentViewModel notifies MainViewModel and passes the data to it:
MainViewModel receives the message and processes it:
Just cancel the registration in MainActivity onDestroy (otherwise it will cause memory leaks):
The above example simply illustrates that Messenger can be used in many scenarios, including notifications and broadcasts. It does not necessarily have to transmit data. Under certain conditions, it can also be used for communication and broadcasting between the View layer and ViewModel. It has a very wide range of applications and requires developers to do deeper exploration in combination with actual business. Summary and source code This article mainly explains some of the Android MVVM construction ideas summarized in the process of personal development, more about how to divide the work of each module and how to design the code in theory. Although there are still relatively few people using the Android MVVM mode for development in the industry, with the release of DataBinding 1.0, I believe that more people will try it in the field of Android MVVM. It just so happens that I have been developing with MVVM for a while recently, and I have some experience, which I write down for reference only. This article and the source code do not involve unit testing. If you need to write unit tests, you can combine Google's open source MVP framework to add a Contract class to implement interface-oriented programming, which can help you write unit tests better. At the same time, MVP and MVVM are not good or bad. The most valuable one is suitable for your business and yourself. It is recommended to combine Google's open source MVP framework and the MVVM-related knowledge introduced in this article to explore a framework suitable for your business development. MVVM Light Toolkit is just a tool library. Its main purpose is to build Android MVVM applications more quickly and conveniently. It adds some extra properties to controls and encapsulates some events. It also introduces the global message channel Messenger. I think it is more convenient to use. You can also try it. Of course, this library still needs to be improved and optimized in many places. It will continue to be updated and optimized in the future. If it cannot meet your business needs, you can clone it and make some related extensions yourself. If you want to learn more about MVVM Light Toolkit, please read my blog post "MVVM Light Toolkit User Guide". The source code address of the project is https://github.com/Kelin-Hong/MVVMLight. Among them: library is the source code of MVVM Light Toolkit. The source code is very simple. Students who are interested can take a look at it. It is not technically difficult and you can add more control properties and event bindings according to your needs. Sample is a demo that implements the Zhihu Daily homepage style. The code examples in this article are all from this demo. The code contains a large part of the usage scenarios of MVVM Light Toolkit (Data, Command, Messenger are all involved). At the same time, the sample is strictly developed according to the MVVM design concept described in this blog post, which will be of great help to understand this article. This article and source code involve RxJava+Retrofit+Lambda. If you don’t understand or have never been exposed to it, take some time to get started. The things used are relatively simple. |
<<: Google AI: No one knows how it works
>>: iOS grabs HTML, CSS XPath parses data
(Original title: Warcraft Movie: The Sunset of a ...
The prospect of continued rapid growth in electri...
When it comes to oil-tea, the first thing that co...
All things are difficult at the beginning. What i...
During the spring equinox, the sun is shining and...
Website defense or network defense is an Internet...
Power Bank Travel Regulations The Civil Aviation ...
Produced by: Science Popularization China Author:...
Why is there no traffic in your live broadcast ro...
Windows 10 is coming this year, and Microsoft has...
App Promotion With the hot sales of Apple mobile p...
Anhui Jinzhai Red Education Base training consult...
This article shares with you 30 high-quality case...
School starts in mid-April at the latest? Which p...