Hi, colleagues. This is the third part of the “Android background in a nutshell” series. Glad to see you here again. If this is the first time you open the series, please check the previous parts:
Last time we figured out how Loaders work and a week after Google announced they have completely rewritten LoaderManager. I suppose I would need to get back to this topic later, but now I am going with my plan and sharing the details of the way how to implement the background work in Android solely with Java’s threadpool executors and how EventBus can help here, and how it all works under the hood.
Let’s recall the main problem: Time consuming operations like network calls should be performed on the background thread, but the result publishing must happen within UI thread only.
Would be also great to save the long running operation’s result during configuration change.
We figured out how it works with AsyncTasks and Loaders in the previous articles. However, that API has it’s drawbacks, making us implement some complex interfaces and abstract classes and preventing from writing pure java modules with async work because of usage of Android platform API.
In a response to such limitations the executors approach emerged. Let’s take a look. First of all, we’re agreeing we need some threads we could post our tasks to in order to perform background job. Let’s create some Background class which does the trick:
So here we have an Executor and a method to submit a task with Runnable or Callable.
Cool, let’s post something on UI thread. No problem, we know all we need is a Handler:
But wait, we don’t know if our UI exists at the moment and if so, how it is supposed to know it should change anything?
This is where EventBus comes in handy. The high-level concept is that you have one(or several) common buses you post events too. Anybody can start listening that bus at anytime, receive the events and then stop listening (sounds a little bit like RxJava, right? Wait for the next part!).
Ok, we basically need only two 3 puzzle pieces here:
- The bus itself
- The event emitter or emiiters
- The event listener or listeners
We can represent the structure in the following diagram:
The Event Bus
Nobody requires you to actually implement the bus itself from scratch, and you can pick one of the implementations out there: Google Guava, Ottoor Greenbot’sone(the latter has the proper fancy annotation-based threading support).
So, you can use the Bus instance directly in your presenters, activities, fragments and so on, but I prefer to incapsulate the thing inside the same Background class:
Let’s write some client code to leverage the construction we’ve just built. Suppose we want to init our database before application usage and it takes sometime. So inside application we start doing work in the background and post an event about the database init have been done:
So for example we can stop the progress bar and proceed to our MainActivity:
The problem here is already well-know to us: we can’t modify the UI from the background thread and the code above tries to do exactly it.
So, we either need to employ the threading abilities of Greenbot library or do it on our own. In production code I beg you not to reinvent the wheel, but for learning purposes, let’s do that with our bare hands; especially it is really simple.
But before let’s dig a little bit and see how the “@Subsribe”-annotated method gets called when the event is posted.
Let’s take a look in the Google Guava Event bus source code.
So, event bus actually stores the subscribers inside SubscriberRegistryand tries to dispatch every event to the subscriber for this specific event(the key here is the object class name). The subscriber registry can be thought as of Map<EventType, Subsriber>.
The threading behaviour depends on the dispatcher object which is set to Dispatcher.perThreadDispatchQueue()by default.
But let’s checkout what happens inside the dispatcher:
The core is here: PerThreadQueuedDispatcher uses ThreadLocal to store the event queue, which effectively means the subscriber method will be called on the same thread the event was posted on.
So, what can we do here?
The solution is rather simple: just post the events on the thread you want them to be handled on:
This works, but introduces the problem EventBus should have solved: decoupling of event sourcing and event handling. With this solution we require the event posting code to know, which thread the client would like to handle the code on.
Other solution would be to use Handlers right on the UI:
Which doesn’t seem to be a proper solution as well. And this is the limitation event bus actually has. How it can be solved? With RxJava of course. But we will find it out in the next part.