Android’s battle with data flow
This article does not have any substantive content, just their work and study this period of some experience miscellaneous, only from their own point of view to elaborate on their own observations of some content, may not be comprehensive, is not necessarily correct, just for your reference.
At present, Android development, in a broad sense, can be classified as part of the front end, after all, most of the work of Android development is to get data from the server, present these data to the user in a suitable way, and then pass the data generated by the user operation to the server. One of the core issues that Android development needs to solve in this context is data transfer, how to associate the server with the page content and keep it in sync. I believe that every engineer has his or her own solution to this process. This process seems simple, but it takes a lot of effort to realize it perfectly.
Wild times
In the early days of Android development, when there was no developed scheme as a reference, a common approach was to create a thread in the Activity, send network requests in the thread, and then use the Handler to pass the result of the request to the main thread. The main thread updated the page after receiving the information. The downside of this approach is obvious, as it can cause an Activity to become bloated and messy as the project grows.
The network library
The core logic of the network request is removed from the Activity, which relieves the Activity. However, another problem arises: how to obtain the result of the network request from the request library. At this point, most network libraries use the most obvious solution, called Callback. When an Activity makes a request, it gets a Callback that returns the result of the network request.
The data layer
But the network request problem solved, but soon another problem became more content, such as: network request and cannot be used directly, the result of the need to do some data conversion, or does not need too high real-time network request, the request once need to be cached, avoid repeating the request. This will require some data conversion or database-related operations within the Activity, which can quickly become bloated.
To avoid becoming bloated by performing these operations directly in an Activity, many people have to encapsulate a data layer between the network request and the Activity. The Activity requests data from the data layer, and the data layer retrieves and transforms data from the network or database as needed. Then hand it to the Activity. How does the data need to be transferred?
Activity <--callbac-1-- data layer <--callback-2-- network requestCopy the code
Typically, an Activity sends a request to the data layer with a Callback, and the data layer sends a request directly to the network with another Callback, passing data from one Callback to another. If the business is a bit more complex, there may be more layers and nodes, so there are not just two callbacks. Data flows between multiple callbacks, and too many callbacks will result in less clear logic. When the project is large enough, All the callbacks in there can confuse people.
EventBus
To solve the complex problem of Callback, EventBus was born. The core of EventBus is actually a publish-subscribe mode. When an Activity needs to obtain some information, it sends an Event, and the data layer performs related operations after receiving the Event. It also tells the Activity the final result by sending an Event. At this time, there is no need to worry about the complicated data transmission mode among various faults. All you need to do is to package the data to be transmitted into one Event after another and send it through EventBus. If relevant events are needed, you can register and listen to the Event in the appropriate place.
So-called event bus, is all the event passed on one place, the principle and the computer hardware bus similar, therefore is called “event bus”, when I first come into contact with this kind of thought, really is amazing, the way to a certain extent, solve the problem of the excessive Callback, Makes the code look cleaner.
But this way is not perfect, it does make the code more concise, but it will be the data transfer process hidden, equivalent to cut off the track circuit of data flow, increased the difficulty of data flow tracking, if the project is the design or his own participation in the first question is unlikely, but if is take someone else’s program is designed so that, The person who takes over is likely to be confused.
Such as: I need the information of the current user, and I know which Event is used to receive the information. However, I find that the received information is sometimes wrong, so I need to find the location where the information was sent and correct the error, which is quite troublesome, because all the information is sent through EventBus. However, I could not directly trace the sending location through EventBus, so I needed to find the Event, track where the Event was used, and then filter out where the error message was sent from these places. For a huge project, this is undoubtedly a huge amount of work. If my predecessors sent this Event in multiple places, I would need to check all the places where this Event was sent, which would waste a lot of working time. As a result, I rarely use EventBus for actual corporate projects, and although it works well, this one flaw can be fatal.
The data flow
When I was struggling with how to transfer data, THE Rx technology solution appeared in front of me. The core idea of Rx is responsive programming, which is a programming paradigm for data flow and change propagation. Data update is related. The idea is similar to that of pipelines in Linux, where data goes from one place to a transformed output that happens to be input to another process, thus concatenating the data.
This is a simple example of taking a blank circle and transforming it into what we want it to look like in two steps, with data flowing from one location to another and constantly changing.
One of the cores of Rx is data streaming, including the introduction of similar streaming ideas in Java8.
The idea of responsiveness is not particularly new, but it has only been realized by code in recent years and has commercial capability. With the maturity of RxJava technology, all data acquisition in the project I participate in is basically processed by RxJava. And based on the powerful operators and thread scheduling capabilities provided by RxJava, it does solve a big problem.
Compared with EventBus, the most important thing in this scheme is the traceability of data flow. As in the example above, if I find any errors in the final result, I just need to follow the data chain and check each link, and I can find the root cause of the problem quickly. For now, this works fine, especially when used with some of the new features in Java8.
If you want to understand how this idea is used, I recommend reading the official documentation of RxJava and Java8 In Action. After reading these, you can basically understand the idea of data flow in responsive programming.
LiveData
LiveData is a technical solution recently introduced by Google that needs to be used with Lifecycle. The core problem it solves is not data transmission, but Lifecycle. We all know that the network is relatively slow, and uncertainty, so the Android does not allow its execution in the main thread, must start another thread, in the thread after get results through some way will the results tell the main thread, the main thread and according to these information is updated, it is because the network returns the time uncertainty has led to the occurrence of a problem, When the network result comes back, the page that might need it no longer exists.
Such as: When I start an activity and launch a network, the result of the network request updates the content in the activity. However, I don’t want the result after I launch the request, so I just click the Back button to shut down the activity. If I forget to cancel the network request when I shut down the activity, it might return a result some time later, and try to update some of the destroyed views in the result, raising an exception and causing the program to crash.
To avoid these problems, we have to add a lot of security checks to the code, and every time something like this happens, the core business logic can be buried in a lot of security checks.
In order to solve this problem, Lifecycle and LiveData were introduced. There were technical solutions that took a similar approach before Lifecycle, but Lifecycle was officially released by Google. Many components in the Support package already support Lifecycle by default, so using Lifecycle and LiveData is much easier.
After the introduction of LiveData, the basic project structure looks like this:
This is a simple structure that is certainly more complex to use in real projects, requiring different technical solutions based on specific business requirements.
After the above changes, the workload of the Activity is greatly reduced. The network, database, and part of the security check logic are removed from the Activity. The Activity only needs to bind the View and control the display content, so it will look particularly concise. It is much more concise when used with Butterknife and Dagger2, but these are beyond the scope of this article, so I won’t cover them.
The above mentioned related technology, and there is no detailed description, if more interested, you can query keywords to understand in detail.
conclusion
As an aside, with the increasing number of Android practitioners, the development of related technologies is also advancing by leaps and bounds. Almost every time, there are new technologies jumping in front of us, such as MVP and MVVM architecture, Kotlin, Flutter and other technologies that have been booming in the last two years. Many people feel like they are being chased by technology, as if if they don’t learn one day, they won’t be able to catch up the next. In this situation, some people are forced to constantly learn these new skills, constantly stock up, for a rainy day. There are also people who actively pursue these new technologies, and when new technologies emerge, they can’t wait to apply them to existing projects.
I personally feel forced to learn or crazy pursuit is not a very good state, every time the emergence of a new technology, must be solved some old technology cannot solve the pain points, there is no need for the first time in the presence of new technology is to learn how to use it, but need to check it first, it is why to solve the problem which, What are the advantages and disadvantages of previous technologies? Only after understanding these situations, we can decide whether to learn according to our own situation. Blind learning may waste our time. Similarly, we can decide whether to introduce this technology according to the project needs of the company.
Finally, the above views are only for my own observation. Due to my limited working time and limited exposure to projects, the above theories are not suitable for all Android projects, and specific situations need to be analyzed.