How to build an Android MVVM application

How to build an Android MVVM application

1. Overview

Databinding is a framework, MVVM is a pattern, the two concepts are different. My understanding is that DataBinding is a framework for implementing data and UI binding, just a tool for implementing the MVVM pattern. ViewModel and View can implement one-way and two-way binding through DataBinding. Google has already done this framework for dynamic monitoring and dynamic updating between UI and data. In the MVVM pattern, ViewModel and View are implemented by binding relationships, so DataBinding makes it possible for us to build Android MVVM applications.

I have read many blogs and related demos about DataBinding before. Most of them are about passing some data into the XML layout file, and then binding these data to the controls (such as TextView binding: text="@{user.name}"), and then setting some events to the controls on these controls (such as Button binding: setOnClickListener="@{user.listener}"). The basic descriptions are all about the basic usage of DataBinding. But no one tells you whether it is not a good idea to write an onClickListener into a class and bind this listener to XML, and no one tells you what data should be placed in the ViewModel class bound to the XML layout, what should it do? How should it be designed? There are even fewer blogs that tell you how to build an MVVM application framework in Android through Data Binding. This is the focus of this article. Next, let's take a look at what MVVM is, and then design the entire application framework step by step.

Source code address https://github.com/Kelin-Hong/MVVMLight

2. MVC, MVP, MVVM

First, let's take a look at the common patterns in Android development so that we can have a deeper understanding of the MVVM pattern.

  • MVC

View: corresponds to the xml layout file

Model: Entity model

Controller: corresponds to Activity business logic, data processing and UI processing

From the above, it seems that the responsibilities of each component are quite coupled with MVC, but when you open an Android Activity file, it is hard to describe. There are often thousands of lines of Activity code in Android. The reason is that the functions of each XML view in Android as a pure View are too weak. Activity is basically a combination of View and Controller. It is responsible for both the display of the view and the control logic. It is not surprising that it has too many functions and a large amount of code. All the current conventional development should be more appropriately described as View-Model mode, most of which are coordinated, connected, and processed through Activity logic.

  • MVP

View: corresponds to Activity and XML, responsible for drawing View and interacting with users

Model: Still a physical model

Presenter: responsible for completing the interaction and business logic between View and Model

The MVP design concept is widely used in Android development. The MVP design model can be used to move some logic codes out of the Fragment and Activity business logic, hold a reference to the View (Activity or Fragment) in the Presenter, and then call the interface exposed by the View in the Presenter to operate the view, which is conducive to separating the view operation from the business logic. MVP can make the Activity a real View instead of a combination of View and Control, and the Activity only does UI-related things. However, this model still has some disadvantages, such as:

  • Activity needs to implement various UI-related interfaces. At the same time, a large number of events need to be written in Activity, and then the business processing methods of presenter are called in event processing. View and Presenter only hold references to each other and make callbacks to each other, and the code is not beautiful.
  • In this mode, the main character of the program is the UI, which processes data through the triggering of UI events, and when updating the UI, threads must be considered. Moreover, the logic coupling involved after the UI changes is too high, and once the control changes (such as replacing EditText with TextView), the interface involved in updating the UI must be changed.

Complex business can also lead to problems such as the presenter layer being too large and the code being bloated.

  • MVVM

View: corresponds to Activity and XML, responsible for drawing View and interacting with users

Model: Entity Model

ViewModel: responsible for completing the interaction between View and Model, responsible for business logic

The goal and idea of ​​MVVM are similar to 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 MVVM, in the previous development mode, business data must be processed first, and then the UI reference must be obtained and updated according to the data changes. User input is also obtained through the UI. In MVVM, data and business logic are in an independent View Model. The ViewModel only needs to focus on data and business logic, and does not need to deal with UI or controls. The data automatically drives the UI to automatically update the UI, and the changes in the UI are automatically fed back to the data. Data becomes the dominant factor, which makes it convenient and simpler to only care about data in business logic processing.

  • Low coupling

In the MVVM pattern, data is independent of the UI. The ViewModel is only responsible for processing and providing data. The UI decides how to process the data. The ViewModel does not involve anything related to the UI and does not hold references to UI controls. Even if the controls change (TextView is replaced by EditText), the ViewModel hardly needs to change any code and can focus on its own data processing. If MVP encounters UI changes, it may be necessary to change the way to obtain the UI, change the interface for updating the UI, change the code for obtaining input from the UI, and may also need to change the code for accessing the properties of UI objects, etc.

  • Update UI

In MVVM, we can modify the data of View Model directly in the working thread (as long as the data is thread-safe), and the rest of the data binding framework will help you, and you don’t need to worry about many things.

  • Teamwork

The division of labor in MVVM is very obvious, because the View and View Model are loosely coupled. One handles business and data, and the other handles UI. It is more efficient to have two people do the work, one doing UI (xml and Activity) and the other writing ViewModel.

  • Reusability

A View Model is reused in multiple Views. The same data can be displayed with different UIs. For UI changes with frequent version iterations, you only need to replace the View layer. This is much more convenient for doing AbTest on the UI.

  • Unit Testing

The View Model contains data and business logic, and the View focuses on the UI. This makes testing very convenient, as there is no dependency between them. Both unit testing of the UI and unit testing of business logic are low-coupling.

Through the above brief description of MVVM and the comparison with the other two modes, we find that MVVM still has relatively large advantages over MVC and MVP. Although there may be very few people who are actually using MVVM in Android development, it is worth our discussion and research.

3. How to build MVVM applications

1). 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 respective responsibilities of View, ViewModel, and Model.

  • View

The View layer does work related to the UI. We only write the View layer code in XML and Activity or Fragment. The View layer does not do anything related to the business, that is, our Activity does not write code related to business logic, nor does it write code that needs to update the UI according to business logic, because updating the UI is implemented through Binding, and updating the UI is done in the ViewModel (updating the bound data source). What the Activity has to do is to initialize some controls (such as the color of the controls, adding the dividing line of the RecyclerView). The Activity can update the UI, but the updated UI must have nothing to do with the business logic and data. It is just to update the UI based on events such as clicks or slides (such as color gradients based on sliding, hiding based on clicks, and other simple UI logic). The Activity (View layer) can handle UI events, but it only handles the UI's own affairs, and the View layer only handles the View layer's affairs. 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 or controls. 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 focuses on the logical processing of the business, and all operations are performed on data. These data sources are bound to the corresponding controls to automatically change the UI, and developers do not need to worry about updating the UI. The DataBinding framework already supports two-way binding, which allows us to obtain data fed back to the ViewModel layer by the View layer through two-way binding and perform operations. Regarding the processing of UI control events, we also hope to bind these event processing to the controls and unify these events to facilitate the ViewModel's processing of events and the beauty of the code. To this end, we encapsulate some common events through BindingAdapter, and encapsulate each event into a Command. For each event, we use a ReplyCommand<T> to handle it. ReplyCommand<T> will bring you the data you may need, so that when we handle events, we only need to care about processing data. For details, see the Command section of the MVVM Light Toolkit User Guide. I emphasize again that ViewModel does not do anything related to UI.

  • Model

The responsibilities of the Model are very simple. It is basically an entity model (Bean) and also includes Retrofit's Service. The ViewModel can obtain a Bean's Observable<Bean> (RxJava) based on the Model, and then perform some data conversion operations and map to some fields in the ViewModel, and finally bind these fields to the View layer.

2) How to collaborate

Regarding collaboration, let’s first look at the following picture:

Figure 1

The above diagram reflects the connection between each module and the direction of data flow in the MVVM framework. From the above diagram, we can see that View and Model are directly decoupled and have no direct connection. That is, as I said before, View does not do anything related to business logic and data processing. Let's split each module one by one. Then our focus is on the following three collaborations.

  • Collaboration between ViewModel and View
  • Collaboration between ViewModel and Model
  • Collaboration between ViewModel and ViewModel

Collaboration between ViewModel and View

Figure 2

In Figure 2, the ViewModel and View are connected together through binding. One type of binding is data binding and the other is command binding. DataBinding has been 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 can quickly display the list by simply binding the ViewModel's data source to the Xml control without writing anything related to Adapter and ViewHolder? 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 use a ReplyCommand<T> to handle it. ReplyCommand<T> will bring you the data you may need, which allows us to only care about processing data when handling events.

From the ViewModel module in Figure 1, we can see that the ViewModel class generally contains the following five parts:

  • Context
  • Model (Data Model Bean)
  • Data Field (Data Binding)
  • Command (Command Binding)
  • Child ViewModel (child ViewModel)

Let's take a look at the sample code first, and then explain what the five parts are used for one by one:

  1. //context
  2.  
  3. private Activity context;
  4.  
  5.   
  6.  
  7. //model (data model Bean)
  8.  
  9. private NewsService.News news;
  10.  
  11. private TopNewsService.News topNews;
  12.  
  13.   
  14.  
  15. //Data binding (data field)
  16.  
  17. public final ObservableField<String> imageUrl = new ObservableField<>();
  18.  
  19. public final ObservableField<String> html = new ObservableField<>();
  20.  
  21. public final ObservableField<String> title = new ObservableField<>();
  22.  
  23. // A variable contains all fields related to View Style
  24.  
  25. public final ViewStyle viewStyle = new ViewStyle();
  26.  
  27.   
  28.  
  29.   
  30.  
  31. //Command binding (command)
  32.  
  33. public final ReplyCommand onRefreshCommand = new ReplyCommand<>(() -> {
  34.  
  35.   
  36.  
  37. })
  38.  
  39. public final ReplyCommand< Integer > onLoadMoreCommand = new ReplyCommand<>((p) -> {
  40.  
  41.   
  42.  
  43. });
  44.  
  45.   
  46.  
  47.   
  48.  
  49. //Child ViewModel
  50.  
  51. public final ObservableList<NewItemViewModel> itemViewModel = new ObservableArrayList<>();
  52.  
  53.   
  54.  
  55. /** * ViewStyle Some properties of the control that are not related to business data can be packaged into a style, so that the code is more beautiful.
  56.  
  57. The ViewModel page will not have too many fields. **/
  58.  
  59. public   static class ViewStyle {
  60.  
  61. public final ObservableBoolean isRefreshing = new ObservableBoolean( true );
  62.  
  63. public final ObservableBoolean progressRefreshing = new ObservableBoolean( true );
  64.  
  65. }

Context

What is Context used for? Why does each ViewModel need to hold a reference to Context? ViewModel does not do anything related to UI, does not operate controls, and does not update UI, so why do we need Context? There are two main reasons. Of course, there are other uses. Calling tool classes and helper classes may require context parameters, etc.:

From Figure 1, we can see that ViewModel passes parameters to Model and then gets an Observable<Bean>. In fact, this is the network request part. To make a network request, we must bind the Observable<Bean> returned by Retrofit Service to the life cycle of Context to prevent exceptions such as Activity being destroyed when the request comes back. In fact, the purpose of this Context is to bind the network request to the life cycle of the current page.

In Figure 1, we can see that the connection between the two ViewModels is done through the Messenger. This Messenger requires the use of Context, which we will explain later.

Model

What is a Model? It is actually a data prototype, that is, a Java Bean converted from Json. We all know that ViewModel may need to copy a lot of Model data to map data to View, and use Model fields to generate corresponding ObservableFields (we will not directly use Model data for display). It is necessary to retain 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 from the list page to the detail page after clicking, and we may need to submit this model to the server as a form. All these require our ViewModel to hold the corresponding model.

Data Field (Data Binding)

Data Field is the ObservableField field that needs to be bound to the control. It is a must for ViewModel. There is nothing much to say about this, but here is a suggestion:

These fields can be classified and packaged slightly. For example, some fields may be bound to some Style attributes of the control (such as: length, color, size), which are dynamically changed according to the changes in business logic. For this type of View Style field, a ViewStyle class can be declared to package it, so that the entire code logic will be clearer. Otherwise, the ViewModel may have too many fields, which is difficult to manage and 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 logic and 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 processed events by getting the reference of UI controls and then setting Listeners. These Listeners are actually Commands. 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 useful method. At the same time, implementing Listener will get the reference of UI, and may do some things related to UI, which is contrary to what we said before that ViewModel does not hold the reference of controls and ViewModel does not change UI. More importantly, to implement a Listener, you may need to write 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 event of loading more, 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 ViewModel’s job 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 will be convenient for you to calculate which page of data should be requested from the server through page=itemCount/LIMIT+1. How great it would be. MVVM Light Toolkit helps you achieve this:

  1. public final ReplyCommand< Integer > onLoadMoreCommand = new ReplyCommand<>((itemCount) -> {
  2.  
  3. int page=itemCount/LIMIT+1;
  4.  
  5. loadData(page.LIMIT)
  6.  
  7. });

Then bind it in the XML layout file through bind:onLoadMoreCommand

  1. <android.support.v7.widget.RecyclerView
  2.  
  3. android:layout_width= "match_parent"  
  4.  
  5. android:layout_height= "match_parent"  
  6.  
  7. bind:onLoadMoreCommand= "@{viewModel.loadMoreCommand}" />

For more information, please refer to the MVVM Light Toolkit User Guide, which explains the use of Command in detail. Of course, Command is not necessary, you can write Listener in ViewModel according to your habits and preferences, but using Command can make your ViewModel more concise and easy to read. You can also define more Commands and other functional Commands yourself, then the event handling of ViewModel is handled by managed ReplyCommand<T>, so the code will look particularly beautiful and clear.

Child ViewModel (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. The Activity itself may have a ViewModel to do its own business. At this time, the ViewModel of the Activity may contain the ViewModels of the two Fragments. This is a nested sub-ViewModel. Another one is for AdapterView such as ListView RecyclerView, ViewPager, etc.

  1. //Child ViewModel public final
  2.  
  3. ObservableList<ItemViewModel> itemViewModel = new ObservableArrayList<>();

Each of their Items actually corresponds to a ViewModel, and then the current ViewModel holds a reference through ObservableList<ItemViewModel> (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 business, 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 all bound in XML. The ViewModel layer will not operate the UI, but only the data. The ViewModel only processes the data according to business requirements, and 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 measured by the developer himself. It is recommended 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, ...). If other types of variables can be avoided, try not to introduce them. Too many member variables will cause great damage to the entire code structure. The person who maintains it later must always pay attention to when the member variables are initialized, when they are cleared, when they are 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.

Collaboration between ViewModel and Model

From Figure 1, the Model obtains network data through Retrofit, and the returned data is an Observable<Bean> (RxJava). This is what the Model layer actually does. Then what the ViewModel does is to obtain network data by passing parameters to the Model layer (the same applies to the database), and then map some of the Model data to some fields (ObservableField) of the ViewModel, and keep the reference of this Model in the ViewModel. Let's take a look at the general code of this part (the code involves simple RxJava, if you don't understand it, you can refer to the introduction):

  1. //Model
  2.  
  3. private NewsDetail newsDetail;
  4.  
  5. private void loadData(long id) {
  6.  
  7. // Observable<Bean> is used to obtain network data
  8.  
  9. Observable<Notification<NewsDetailService.NewsDetail>> newsDetailOb =
  10.  
  11. RetrofitProvider.getInstance()
  12.  
  13. .create (NewsDetailService.class)
  14.  
  15. .getNewsDetail(id)
  16.  
  17. .subscribeOn(Schedulers.io())
  18.  
  19. .observeOn(AndroidSchedulers.mainThread())
  20.  
  21. // Bind network requests to the Activity lifecycle
  22.  
  23. .compose(((ActivityLifecycleProvider) context).bindToLifecycle())
  24.  
  25. //Change to Notification<Bean> to make it easier for us to handle data and errors
  26.  
  27. .materialize().share();
  28.  
  29. // Process the returned data
  30.  
  31. newsDetailOb.filter(Notification::isOnNext)
  32.  
  33. .map(n -> n.getValue())
  34.  
  35. // Assign a value to the member variable newsDetail, one of the five variable types mentioned above (model type)
  36.  
  37. .doOnNext(m -> newsDetail = m)
  38.  
  39. .subscribe(m -> initViewModelField(m));
  40.  
  41. // Network request error handling
  42.  
  43. NewsListHelper.dealWithResponseError(
  44.  
  45. newsDetailOb.filter(Notification::isOnError)
  46.  
  47. .map(n -> n.getThrowable()));
  48.  
  49. }
  50.  
  51. //Model -->ViewModel  
  52.  
  53. private void initViewModelField(NewsDetail newsDetail) {
  54.  
  55. viewStyle.isRefreshing.set ( false ) ;
  56.  
  57. imageUrl.set (newsDetail.getImage());
  58.  
  59. Observable.just(newsDetail.getBody())
  60.  
  61. .map(s -> s + "<style type=\"text/css\">" + newsDetail.getCssStr())
  62.  
  63. .map(s -> s + "</style>" )
  64.  
  65. .subscribe(s -> html. set (s));
  66.  
  67. title.set (newsDetail.getTitle());
  68.  
  69. }

The above code basically completes the comments, the basic idea is relatively clear, and the operators involved in Rxjava are relatively basic. If you don’t understand, you can take a quick look. The ViewModel data logic processing in the subsequent source code is done with Rxjava, so you need to learn it in advance to make it easier for you to understand the source code.

Note: We recommend using MVVM and RxJava together. Although both have the concept of observer mode, we do not use RxJava 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.

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. It can also be used for communication between View and ViewModel, but 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 only:

The scenario is this: your MainActivity corresponds to a MainViewModel. In addition to its own content, MainActivity also contains a Fragment. The business processing of this Fragment corresponds to a FragmentViewModel. FragmentViewModel requests the server and obtains data. It happens that MainViewModel also needs this data. We cannot request data again in MainViewModel, which is not reasonable. At this time, we need to pass the data to MainViewModel. How should we pass it? There is no reference or callback between them. Then we can only use the global message channel Messenger. After receiving the message, FragmentViewModel notifies MainViewModel and passes the data to it:

  1. combineRequestOb.filter(Notification::isOnNext)
  2.  
  3. .map(n -> n.getValue())
  4.  
  5. .map(p -> p. first )
  6.  
  7. .filter(m -> !m.getTop_stories().isEmpty())
  8.  
  9. .doOnNext(m ->Observable.just(NewsListHelper.isTomorrow( date )).filter(b -> b).subscribe(b -> itemViewModel.clear()))
  10.  
  11. // You don't need to look at the above code, it just gets the network data and passes the data through send
  12.  
  13. .subscribe(m -> Messenger.getDefault().send(m, TOKEN_TOP_NEWS_FINISH));
  14.  
  15. MainViewModel receives the message and processes it:
  16.  
  17. Messenger.getDefault().register(activity, NewsViewModel.TOKEN_TOP_NEWS_FINISH, TopNewsService.News.class,(news) -> {
  18.  
  19. // to something....
  20.  
  21. }

Just cancel the registration in MainActivity onDestroy (otherwise it will cause memory leak)

  1. @Override
  2.  
  3. protected void onDestroy() {
  4.  
  5. super.onDestroy();
  6.  
  7. Messenger.getDefault().unregister(this);
  8.  
  9. }

Of course, the above example is just a simple explanation. Messenger can be used in many scenarios, such as notification and broadcast. It does not have to transmit data. Under certain conditions, it can also be used for communication and broadcast between the View layer and ViewModel. The application range is particularly wide, and developers need to do deeper exploration in combination with actual business.

4. Summary and source code

This blog post mainly explains some 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 in the field of Android MVVM. It happens that I have been developing with MVVM for a while recently, and I have some experience. I write it down for reference only.

The process code explained in this article is relatively small. The code uses a MVVM Light Toolkit library developed by myself, and it is RxJava + Lambda code. I guess many people will be dizzy when reading it. The source code will be released here. If you have not tried to build an Android MVVM application with RxJava+Retrofit+DataBinding, then you can try to look at the source code here and give it a try. Maybe you will like this development framework.

About 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. It is really convenient to use. You can try it. Of course, there are still many places that are not perfect and optimized. They will be continuously updated and optimized in the future. If it cannot meet your business needs, you can also add the properties and events you need yourself. If you want to learn more about MVVM Light Toolkit, please read my blog post MVVM Light Toolkit User Guide

Source code address https://github.com/Kelin-Hong/MVVMLight

library —> 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. There is not much technical difficulty. You can add more control properties and event bindings according to your needs.

sample —> The codes involved in this article are all from this project. The sample is a simple implementation of a Zhihu Daily App. The code contains a large part of the usage scenarios of MVVM Light Toolkit (Data, Command, and Messenger are all involved). At the same time, the sample is developed strictly in accordance with the design concept of MVVM described in the blog post, which is very helpful for understanding this article. You are welcome to clone it and take a look.

Sample Screenshots

The source code involves 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. I hope this blog will help you in how to build Android MVVM applications. If you have any questions, you can leave me a message. Welcome to discuss together. If you have any questions about MVVM Light Toolkit, you can also give me feedback.

<<:  Top 3 machine learning libraries for Python

>>:  Analysis of the technical principles of mobile terminal monitoring system

Recommend

Did aliens really draw a circle in the desert?

In the desert grasslands of Namibia in southern A...

8 ways to acquire customers for B2B products!

What is the difference between 2B products and 2C...

Year-end review: Interpretation of the top ten domestic technology news in 2021

The year of 2021, with its climaxes one after ano...

Programmatic Advertising Quantitative Evaluation Revenue Indicator - eCPM

This article will share with you how to improve &...

Feet: Don’t tell my mouth, I can taste garlic too!

As the saying goes: If you don't eat garlic, ...

It's outrageous: I'm 165cm tall and weigh 80kg, but I have fatty liver...

During the physical examination at the end of the...

Why are used car e-commerce companies so keen on "advertising"?

Starting from a few days ago, you can already see...

What do Douyin weight and activity mean? How to improve?

When we first started using TikTok, we often comp...