preface

Recently, IN the background, I have received a message from readers, asking if I could release a Jetpack MVVM with a concise case, so that I can play, feel and deepen my impression on MVVM.

The answer is yes.

Standardization oriented development has become a reality

Gold nine silver ten, I believe there are many readers to seize the opportunity to interview.

The Android market isn’t what it used to be. In the past, candidates were under pressure to find a good job by understanding four components, views, and network requests.

Now, with the Jetpack architecture components and standardized development models in place, Android development has come of age:

A lot of boilerplate code no longer needs to be written by developers, but can be generated automatically through template tools, in order to eliminate the tedious and time-consuming repetitive work, and avoid the negligence of manual operation, which will cause difficult to detect and unpredictable errors.

This is in the best interest of the organization, and interviewers are looking for candidates with an understanding of architecture components — at least MVVM — when hiring.

Vague terms like “decoupling” are no longer accepted by interviewers, and any interviewer with even a hint of MVVM experience will ask for examples of how you really have the right, deep understanding of MVVM to write standardized, standardized code naturally. Can quickly adapt to each company’s own automated template tools.

Objective of this paper

I have three years of experience in mobile terminal architecture practice and design, and have led or participated in more than ten large and medium scale projects reconstructed by the team. I have a deep understanding of the efforts made by Jetpack MVVM architecture in establishing a standardized and standardized development mode to reduce unexpected errors.

Therefore, the goal of this paper is to combine with the in-depth and simple introduction of Lifecycle, LiveData, ViewModel and DataBinding in the previous issues to give a comprehensive interpretation:

What efforts does the standardized state management framework, the application development skeleton, make to reduce unexpected errors during rapid development?

Different from the patchwork, echoing what others say, and disturbing web articles, it is the only one in the whole network that is willing to share its deep thinking knowledge and practical reflective experience of standardized development mode without reservation. This is one article to read, one to lose, so be sure to follow along and walk through Jetpack MVVM without a problem, even if you don’t hold your interviewer’s hand!

List of articles

  • preface
  • Standardization oriented development has become a reality
  • Objective of this paper
  • Jetpack Lifecycle
    • The chaotic world before Lifecycle exists
    • Why does Lifecycle solve these problems?
  • Jetpack LiveData
    • The chaotic world before LiveData existed
    • Why does LiveData solve these problems?
    • LiveData has a hole to be aware of
    • Note: 2020.07.09
  • Jetpack ViewModel
    • The chaotic world before ViewModel existed
    • Why does the ViewModel do all this?
  • Jetpack DataBinding
    • The chaotic world before DataBinding existed
    • DataBinding is designed to solve these problems
  • To sum up

Jetpack Lifecycle

Lifecycle exists mainly to solve the consistency problem of Lifecycle management

The chaotic world before Lifecycle exists

Before Lifecycle came to market, Lifecycle management was maintained purely by hand, which led to a lot of consistency issues.

For example, the GpsManager component, which is shared across pages, needs to be manually activated, unbound, and suspended in the onResume and onPause of each Activity that depends on it.

The consistency pitfalls of this manual operation grow exponentially as the number of activities increases:

On the one hand, it’s easy to neglect things that are done manually, especially when the work is handed over to other colleagues who don’t notice the details in time.

On the other hand, scattered code is not good for modification, and each Activity needs to be written again if there are other actions (such as status listening) that need to be supplemented in addition to activation and suspension.

Why does Lifecycle solve these problems?

Lifecycle uses the template method pattern and observer pattern to wrap the complex operations of Lifecycle management in a base class for the LifecycleOwner (for example, the base class for the view controller), quietly working behind the scenes for the developer.

This allows developers to gracefully complete the third-party component’s own internal awareness of the LifecycleOwner lifecycle with a single getLifecycle().addobServer (gpsManager.getInstance) in the view controller (subclass).

In addition to solving the consistency problem, this provides two other benefits by the way:

1. Avoid injecting view controllers to listen for state

When we need to listen for state, we used to manually inject parameters such as an Activity through methods, which created a potential memory leak because a new member of the team could erroneously rely on an Activity to another member of the component.

Today, you can avoid this kind of inappropriate use by listening directly to the LifecycleOwner’s state from within the component.

2. Avoid injecting view controllers to trace the source of the incident

In the past, if we wanted to trace the source of the accident in the component, we had to inject the Activity directly from the method, which also buried the hidden danger of memory leak. Now that components implement a DefaultLifecycleObserver, the LifecycleOwner parameter in the lifecycle callback method can be used to know the source of the incident in the method scope, eliminating the need for more hidden actions.

If that doesn’t make any sense then refer to the GpsManager example I provided at Jetpack Lifecycle to give you a real Life. I won’t go back to that in this article.

Jetpack LiveData

LiveData exists primarily to help newcomers and veterans to follow the standardized development concept of distributing state through a single trusted source without thinking, thus minimizing the probability of difficult to trace, difficult to troubleshoot, and unexpected problems during rapid development.

The chaotic world before LiveData existed

Before LiveData came out, we distributed status, mostly through EventBus or Java Interface. Whether you’re using it for network request callbacks or cross-page communications.

So what’s the problem with that? First of all, EventBus is a pure Bus, which lacks the aforementioned standardized development concepts, so people are prone to decentralize abuse when using the framework. This leads to situations such as receiving unexpected pushes from unknown sources, getting outdated data, and having a complexity of n ^ 2 in tracing events to sources.

In addition, EventBus itself lacks the support of Lifecycle, which leads to the consistency problem of Lifecycle management. This was the bane of EventBus, and the main reason I refused to use EventBus.

For those who don’t understand the above situation, please refer to the global notification of player status that I provided in The Little-known Background and Unique Mission of LiveData

Why does LiveData solve these problems?

First of all, LiveData was born in the context of Google’s desire to establish a standardized and standardized development model, so in order to achieve this difficult mission, LiveData was designed to support only state input and monitoring. And can be based on “access control” to achieve “read and write separation”.

This allows any data push to be limited to being “pushed unilaterally from a single trusted source,” thereby avoiding inconsistent synchronization, unreliability, or wasting time in an n ^ 2 maze of event traceability. (that is, no matter which view controller initiates the request for a shared state change, the final state change is decided internally by the singleton or SharedViewModel as the only trusted source, and the change is notified one-to-many)

In addition, this connection makes one-way dependency possible: the singleton does not need to notify the view controller through Java Interface callback, thus avoiding the memory leak hidden by the longer lifetime singleton dependency of the view controller.

LiveData has a hole to be aware of

However, there is a hole in the LiveData design, which I’ll mention here in passing.

LiveData is designed to be sticky events in order to automatically restate the last data observed in LiveData after a view controller reconstruction occurs.

— I’m going to say that this is a poorly developed design, or even a bug,

Because viewModels support shared scopes, and official documentation recommends sharing ViewModels to meet cross-page communication requirements,

Then, based on the “open closed principle,” LiveData should provide a parallel underlying support to MutableLiveData for non-sticky event communication situations, Otherwise, using MutableLiveData directly in cross-page communication will cause event callback consistency problems and unexpected errors.

For non-sticky LiveData implementations, there are two ways to solve the problem online: “event wrapper class” (for kotlin only) and “reflection intervention LastVersion” (for Java) :

Using LiveData in SnackBar and Other Events (SingleLiveEvent Case)

#LiveData extension (reflection case)

Regardless of the implementation, I recommend following the development philosophy of traditional LiveData to ensure that message synchronization is predictable and traceable by distributing state from a single trusted source. As for the “decentralized” approach to Bus, I refuse to use it in this project.

Note 2020.07.09:

The Event wrapper Noninvasive rewrite UnPeekLiveData

Considering the hand-written Event wrapper, there are null-safe consistency issues in Java; The reflection intervention Version has delays (not available for real-time scenarios), and the data stays in memory for a long time with the SharedViewModel.

Unpeek-livedata was rewritten and encapsulated specifically for the “one-time event” scenario requirements.

Unpeek-livedata has evolved to meet the following requirements:

  1. A message can be consumed by multiple observers
  2. Only after the message has been consumed by all observers does backflow stop begin
  3. Messages can be manually removed from memory using the clear method
  4. Make non-invasive design possible, following the open closed principle
  5. Supports “read/write separation” based on “access control” and follows the message distribution concept of only trusted source

Specific can refer to “UnPeek-LiveData” the latest source code.

Jetpack ViewModel

Viewmodels exist primarily to solve the problems of state management and page communication.

The chaotic world before ViewModel existed

The ViewModel’s job is to divide and conquer state management and state management, which means that when the view controller is rebuilt,

For lightweight state, storage and recovery can be done in a serialized manner through the saveInstanceState mechanism of the view controller base class.

Heavy state, such as a List obtained through network requests, can be held by a ViewModel with a longer lifetime than the view controller, allowing it to be recovered directly from the ViewModel, rather than in a less efficient serialization manner.

Prior to the Jetpack ViewModel, neither MVP Presenter nor MVVM-Clean ViewModel had state-management divisible capabilities.

Presenter and Clean ViewModel both live and die with view controllers, so they are at best hosting state for the DataBinding, not divide-and-conquer state.

With this release of Jetpack, the ViewModel has achieved state management and shareable scope in an elegant design.

Why does the ViewModel do all this?

In fact, this moderator is mainly based on the factory mode, making the ViewModel owned by the LifecycleOwner and referenced by the ViewModelProvider,

So it is both similar to a singleton: — When held by an Activity as LifecycleOwner, it can be shared scoped out of the lifecycle of its Fragment,

It’s actually not a singleton: — The life cycle follows the view controller as LifecycleOwner, which is cleared when the Owner (Activity or Fragment) is destroyed.

In addition, Google has retained the ViewModel in the view controller base class through the retain mechanism for view controller reconstruction.

Thus, in the case of scope sharing and view rebuilding, state is preserved intact and can be used directly by the view controller on recovery.

Furthermore, the ViewModel itself is responsible for cross-page communication (such as event callbacks) because of shared scope considerations. The stickiness design of Event communication in LiveData was already discussed earlier in the introduction to LiveData and will not be covered here.

Note: As of 2020.2.1, the retain design of ViewModel in Fragment has changed dramatically. For details, please refer to my book Jetpack ViewModel… The latest additions at the end of the article and in the comments section.

Jetpack DataBinding

DataBinding exists primarily to solve the consistency problem of view calls.

The chaotic world before DataBinding existed

To change the state of a view before the DataBinding is released, we first need to reference the view, such as textView.settext (),

What’s the problem?

When a page has a horizontal and vertical layout and the two layouts have different controls, such as a textView in landscape and not in portrait, then we have to empty textView in the view controller, which causes consistency problems. After all, there are dozens of pages and countless places for each page to invoke the control.

So what do we do?

DataBinding is designed to solve these problems

By having controls present in the layout bind to observable data, the controls bound to that data are notified and refreshed when that data is set with new content.

In other words, the only change after using DataBinding is that you don’t need to manually call the view to set the new state, you just need to set the data itself.

Thus, DataBinding is not, as many people unthinkingly think, hard to debug by writing UI logic in XML — it’s not at all:

DataBinding is only responsible for binding the data, for changing the state at the end of the UI logic (i.e., it’s a nondivisible atomic operation that doesn’t need debugging), and for doing what the UI logic used to do in the view controller. Textview.settext (XXX) is no longer required, but xxx.set().

So with the help of DataBinding, what are the total benefits?

1. Avoids the consistency problem of view calls — no need to manually empty.

2. Avoids the consistency of view calls, and even doesn’t need to call the view manually, so you don’t need to write findViewById at all.

3. If you want to call a view, do not use findViewById, but directly refer to it with a binding.

4. The previous UI logic is largely unchanged, except for the way state changes at the end.

In addition, DataBinding also has a BindingAdapter that provides custom properties for its controls. It not only solves the problem of reusing drawables with rounded corners (you know), but also allows imageViews to be directly bound to urls, etc. There’s nothing you can’t do, but the DataBinding benefits are just waiting to be discovered.

For tips on DataBinding DOS and don ‘ts, tried-and-true pit techniques, and exclusive parsing of the “DataBinding strict mode,” see From Misunderstood to Fragrant Jetpack DataBinding! I will not elaborate here.

To sum up

Lifecycle exists mainly to solve the consistency problem of Lifecycle management.

LiveData exists primarily to help newcomers and veterans to follow the standardized development concept of distributing state through a single trusted source without thinking, thereby avoiding a series of problems that are difficult to trace, difficult to troubleshoot, and unpredictable during rapid development.

Viewmodels exist primarily to solve the problems of state management and page communication.

DataBinding exists primarily to solve the consistency problem of view calls.

Most of them exist to solve the problem of consistency in the context of software engineering, to encapsulate error-prone operations in the background, and to facilitate users to code quickly, statically and without unexpected errors.

The full text after

GitHub : Jetpack-MVVM-Best-Practice

Copyright statement

This article is signed by CC – non-commercial – prohibited for distribution under 4.0 international agreements.

Copyright © 2019 – present KunMinX

As mentioned in the article, “the existence of XXX architecture components is mainly to solve the consistency problem of XXX in the context of multi-person collaboration in software engineering”, and “Data inversion occurs in LiveData in the scenario of page communication and event callback”, etc., match and summarize specific phenomena and their essence. Are my independent original results, I enjoy the ownership and the final interpretation right.

When you use or quote the introduction, ideas, conclusions of this article for secondary creation, or reprint in full text, you must indicate the link source, otherwise we reserve the right to blame.

Without the permission of face-to-face communication with the author, the contents of the article shall not be used for commercial purposes such as manuscript development, advertising and packaging.