In the last chapter of this series, I want to talk about the Eventbus of GR, how simple and effective it is to handle multithreaded asynchronous tasks.
Asynctask, loader and executor ... Come on!
There are many ways to perform asynchronous operations in Android (referred to as parallel to the UI thread). Asynctask is the simplest mechanism for the user and requires only a small amount of setup code. However, its use is limited, as described in the official Android documentation:
Asynctask is designed to be a tool class that contains thread and handler inside it, but it is not a part of the generic threading framework itself. Asynctask should be used as much as possible to perform some shorter operations (up to a few seconds). If you need to perform long tasks in a thread, it is recommended that you use the various APIs provided in the Java.util.concurrent package directly, such as executor, Threadpoolexecutor, and Futuretask.
But even short-term operations can cause problems, especially in areas related to the activity/fragment life cycle. Since the Asynctask will continue to run (even if the activity/fragment that started them have been destroyed). This way, once you try to update the UI in the OnPostExecute method, it will eventually cause a IllegalStateException exception to be thrown.
The loader API was introduced in Android 3.0 to solve the Activity/fragment life cycle problem (they are really effective). The Loader API is designed to load data asynchronously into the activity/fragment. Although loading data is a very common asynchronous operation, it is not the only operation that needs to be separated from the UI thread. Loader also needs to implement another listener interface in the activity/fragment. Although this is not a mistake, I personally do not like the pattern (I mean that eventually your code will contain many callback functions, resulting in poor readability of the code). Finally, activity and fragment are not the only places where the asynchronous operations need to be split into threads. For example, if you're in a service, you can't access Loadermanager, so eventually you'll have to use Asynctask or java.util.concurrent.
The Java.util.concurrent package is good and I can use it in both Android and non-Android projects. However, it needs to be configured and managed in a little bit, not as simple as asynctask. You need to initialize the Executorservice, manage and monitor its life cycle, and may need to deal with some future objects.
Asynctask, loader and executor are very effective as long as they are used properly. But in complex applications, you need to choose the right tool for each task, and you'll probably end up with three of them. This allows you to maintain three different framework code for handling concurrency.
Green robot to help!
A very good concurrency processing mechanism is built into the Eventbus of Gr. In the listener class, you can implement 4 different types of processing methods. When a matching event is sent over, Eventbus sends the event to the appropriate processing method according to the different concurrency models:
- OnEvent (T Event): Runs in the same thread as the event being sent.
- Oneventmainthread (T Event): Runs in the primary (UI) thread, regardless of the thread from which the event was sent.
- Oneventasync (T Event): Runs in a separate thread, either a non-UI thread or a thread that sends an event.
- Oneventbackgroundthread (T Event): If the thread that sent the event is not the UI thread, it is running in that thread. If the event is sent by the UI thread, it runs in a separate thread maintained by Eventbus. Multiple events are handled synchronously by this separate background thread.
These methods are powerful and simple to use. For example, there is a time-consuming operation (probably a network call, a lot of data processing, and so on), which needs to be triggered by the behavior on the UI, and the UI should be updated when the operation is finished. In this example, the UI behavior is a button click, and the button is in the activity, time-consuming operation in the service. We can do this as follows:
Java
| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 |
someactivity. Java: . . .@Inject eventbus bus; . . .@Overrideprotected void onCreate(Bundle savedinstancestate) { Super. OnCreate(savedinstancestate); . . .button. Setonclicklistener(new View). Onclicklistener() { public void onClick(View v) { Eventbus. Post(new dooperationevent()); }});} @Overrideprotected void onresume() { Super. Onresume(); bus. Register(this); }@Overrideprotected void onPause() { bus. Unregister(this); Super. OnPause(); } public void oneventmainthread (operationcompleteevent event) { Toast. Maketext(this, "Operation Complete!" , Toast. Length_short). Show(); }. . .someservice. Java: . . .@Inject eventbus bus; . . .@Overrideprotected void onCreate() { Super. OnCreate(); bus. Register(this); }@Overrideprotected void OnDestroy() { bus. Unregister(this); Super. OnDestroy(); }public void oneventasync(dooperationevent event){ //Do SOMETHING long-running hereEventbus. Post(new operationcompleteevent()); }. . . |
Although this example is simple, it is a very concise explanation of the problem. There is no need to implement a listening interface, and there is no such thing as a life cycle (because the activity can only receive the Operationcompleteevent event when it exists). In addition, if there is a configuration change (rotating the screen) or whatever causes the activity to be destroyed and rebuilt between the two occurrences, the Operationcompleteevent event can eventually be received.
In addition, we can easily think of some other usage. For example, if you need to send the update progress, you just have to implement an event class that encapsulates the progress value and then send it out. Alternatively, if you want other events (whether the same or different types) not to be processed in parallel (synchronous execution), you can choose to use Oneventbackgroundthread.
Rely on bus
The simplest way to instantiate Eventbus is through Eventbus.getdefault (). However, other useful configuration methods are included in the Eventbusbuilder class (obtained through Eventbus.builder ()). Especially the use of your own executorservice mentioned in this article. By default Eventbus creates its own executorservice through Executors.newcachedthreadpool (), which in most cases satisfies your needs. However, sometimes you may still want to show control over the number of threads used by Eventbus, in which case you can initialize the Eventbus as follows:
Java
| 1 |
Eventbus. Builder(). Executorservice(executors. Newfixedtheadpool(num_threads)). Installdefaulteventbus(); |
Some of the other things that can be configured in Eventbusbuilder are controls that are related to exception handling, and a control switch that allows event classes to be inherited. These things are beyond the scope of this article, but I suggest you study them carefully. GR may not have written these in the documentation, but if you read the source code of Eventbusbuilder and Eventbus, you will be able to understand them easily.
All-in-one programmer Exchange QQ Group 290551701, gather a lot of Internet elite, technical director, architect, Project Manager! Open source technology research, Welcome to the industry, Daniel and beginners are interested in engaging in IT industry personnel to enter!
Use of Eventbus in Android (3): Multithreading event handling