Android background tutorial. Part 3: Executors and EventBus
- Transfer

Greetings, colleagues. Glad to see you again in the third part of the Android Background Guide. If you have not seen the previous parts, here they are:
Last time we figured out how Loaders work, and right after that, Google picked up and announced that they completely rewrote LoaderManager. Apparently, I need to return to this topic later, but for now I will follow the plan and share the details of how to organize background work in Android exclusively using Java thread pool executors, as well as how EventBus can help with this, and how it all works under the hood.
Let's remember what the main catch is: time-consuming operations like network calls should be carried out in the background thread, but the publication of the result can only occur in the main UI thread.
It would also be great to keep the result of a long operation when changing the configuration.
In previous texts, we figured out how to do this with AsyncTasks and Loaders. However, these APIs have their drawbacks, which make it necessary to implement fairly complex interfaces and abstract classes. In addition, they do not allow us to write modules with asynchronous operation in pure Java due to the use of Android-specific APIs.
Because of these limitations, an approach based on executors has emerged. Let's look at it. To begin with, of course, we need to get hold of the threads where we can send our tasks for background work. Let's create the Background class for this:
public class Background {
private final ExecutorService mService = new ScheduledThreadPoolExecutor(5);
public Future execute(Runnable runnable) {
return mService.submit(runnable);
}
public Future submit(Callable runnable) {
return mService.submit(runnable);
}
}
So, we have executor, and there is a method that allows you to run some code asynchronously, wrapping it in Runnable or Callable.
Great, let's try to push the result of the operation into the UI stream. Not a problem, we know that we only need a Handler:
public class Background {
...
private final Handler mUiHandler;
public void postOnUiThread(final Runnable runnable) {
mUiHandler.post(runnable);
}
}
But wait, we don’t know if our UI exists at this moment, and if so, how does it know that something needs to be changed?
This is where the approach called the "event bus" or event bus comes to the rescue. The general idea is that there is a certain common bus (or even several) where events are published. Anyone can start listening to the bus at any time, then receive events, and then stop listening (sounds like RxJava, right? Wait for the next article!)
In general, we need three components:
The bus itself
Source (or sources) of events
Listener (or listeners ) events
You can reflect this structure with such a diagram:

Schematic diagram of the transmission of events on the bus
Event bus
No one needs to implement a bus from scratch on their own. You can choose one of the existing implementations: Google Guava , Otto or EventBus from greenrobot (the latter has stylish support for sending events to different threads using annotations).
We can use the bus object directly in our presenters, activities, fragments, and so on, but I prefer to encapsulate it in the same Background class:
public class Background {
private final Bus mEventBus;
public void postEvent(final Object event) {
mEventBus.post(event);
}
}
Let's write client code using the construct we built. Suppose we want to initialize the database before using the application, and this, of course, takes time. So inside the application we start initialization in the background and publish an event that the initialization of the database has completed:
mBackground.execute(new Runnable() {
@Override
public void run() {
try {
initDatabaseInternal();
mBackground.post(new DatabaseLoadedEvent());
} catch (Exception e) {
Log.e("Failed to init db", e);
}
}
});
So, for example, we can hide the progress bar and go to our MainActivity:
public class SplashActivity extends Activity {
@Override
protected void onStart() {
super.onStart();
eventBus.register(this);
}
@Override
protected void onStop() {
eventBus.unregister(this);
super.onStop();
}
@Subscribe
public void on(DatabaseLoadedEvent event) {
progressBar.setVisibility(View.GONE);
showMainActivity();
}
}
The problem here is already well known to us: you cannot modify the UI from the background thread, and the code above tries to do just that.
So we need to either take advantage of the work with the stream from the greenrobot library, or do everything ourselves. You should not reinvent the wheel in a production application, but for training purposes, let's try to do it with bare hands, especially since it is very simple.
But before that, let's dig in a little bit of sorts and see how the method marked with the Subscribe annotation is called when the event is published.
Take a look at the source code for the Google Guava event bus:
public class EventBus {
private final SubscriberRegistry subscribers = new SubscriberRegistry(this);
public void register(Object object) {
subscribers.register(object);
}
public void unregister(Object object) {
subscribers.unregister(object);
}
public void post(Object event) {
Iterator eventSubscribers = subscribers.getSubscribers(event);
if (eventSubscribers.hasNext()) {
dispatcher.dispatch(event, eventSubscribers);
} else if (!(event instanceof DeadEvent)) {
// the event had no subscribers and was not itself a DeadEvent
post(new DeadEvent(this, event));
}
}
}
As you can see, the event bus stores subscribers in SubscriberRegistry and tries to pass each event to the subscriber of this particular event (the key is the name of the object class). The list of subscribers can be imagined in the form of a Map.
The handling of threads depends on the dispatcher object, which is set to Dispatcher.perThreadDispatchQueue () by default .
What happens inside the dispatcher:
private static final class PerThreadQueuedDispatcher extends Dispatcher {
private final ThreadLocal> queue =
new ThreadLocal>() {
@Override
protected Queue initialValue() {
return Queues.newArrayDeque();
}
};
@Override
void dispatch(Object event, Iterator subscribers) {
Queue queueForThread = queue.get();
queueForThread.offer(new Event(event, subscribers));
Event nextEvent;
while ((nextEvent = queueForThread.poll()) != null) {
while (nextEvent.subscribers.hasNext()) {
nextEvent.subscribers.next().dispatchEvent(nextEvent.event);
}
}
}
The main thing here: PerThreadQueuedDispatcher uses ThreadLocal to store the event queue. In essence, this means that the subscriber method will be called in the same thread in which the event was posted.
And what shall we do with this? The simple solution is to simply publish events in the stream in which you want to process them:
public void postEventOnUiThread(final Object event) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
mEventBus.post(event);
}
});
}
This works, but introduces the problem that the event bus seems to solve: lowering connectivity by separating publication and event handling. With this solution, we oblige the code that publishes the events to know in which thread the client would like to process the code.
Another solution would be to use Handlers directly in the UI:
public class SplashActivity extends Activity {
@Subscribe
public void on(DatabaseLoadedEvent event) {
runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setVisibility(View.GONE);
showMainActivity();
}
})
}
}
This also does not look like a complete solution. And that is the limitation of the event bus. How can this be dealt with? Of course using RxJava! But more about that in the next part.
From the Author: I enter the program committee of the Mobius Conference, and its program is 90% complete. Rather, see what the conference has prepared for you , and wait for the news about the finalization of the program!