Threading Strategies On Android With Clean Architecture
First Approach: Use Cases
The main advantage of having this base class is that once you have it there, you get this seamless blanket where everything in the presentation layer happens on the UI thread everything in the use case and the gateways on a background thread. Theoretically, one does not have to care about threading anymore. But that seems to not be as straight forward as we thought.
Problems With the Use Case Approach
The issues started when we needed to do something in a Gateway on the main thread (the UI thread). It was a location provider that used the Google Location API. In the Google API, the creation of the API client needs to happen on the same thread as the connect method. The creation happened in the constructor and the constructor was called by Dagger when you want to inject this dependency. In this case it happened in the home activity’s onCreate — meaning in the UI thread. The location request happened from a use case that needed it — meaning in a background thread. The main problem is, when dealing with gateways, there is no one size fits all. Each gateway might have a more nuanced threading needs that this simplistic approach does not cater for. Not to mention if you have some animation related calculations that you might want to perform on a background thread. In these cases, you might need to do some asynchronous work in the presentation layer.
Even if you work around this issue, there is the question of responsibility. Is it really the use case’s responsibility to handle threading? So which component needs to be responsible for that? I will answer that further in this post…
Another aspect of this dilemma is the ideas behind the Clean Architecture approach. One of the main points Uncle Bob makes when he presents this architecture is that what your software is doing should be the main focus and the rest is an implementation detail (DB, GUI etc). I would argue that threading needs fall under the same category. The fact that UI manipulation has to happen on the main thread in Android is just an implementation detail of what Uncle Bob refers to as the Delivery Mechanism. Same goes for how we obtain the device’s location or any other device specific information.
So Where To Handle Threading?
After watching the excellent presentation by Rich King titled Building a Framework with Clean Architecture I asked him how do they deal with threading and he told me about a very elegant solution that they implemented that I think also addresses all of the issues I mentioned above.
The idea is simple, each gateway handles its own threading needs and the same goes for the presentation layer. So, the presenters (I referred to the presentation layer pattern in my previous post) will look something like this:
As you see, the UI manipulation happens on the main thread regardless of what goes on in the use case.
The gateway’s code would look something like this:
So all the access to the DB in this case will happen on the IO thread or any other thread of your choosing.
What I personally like about this solution is that it ticks all the boxes. The responsibilities are clear: each component is in charge of its own threading needs and the use cases are agnostic to that. Also, as far as Clean Architecture concepts go, the domain layer remains clean of any implementation details of the outer layers. Finally, it fixes the issues we encountered by giving the outer layers full control over their threading strategy and thus eliminating the need for nasty workarounds.
One More Thing…
While talking with Rich, he mentioned that they are moving away from that approach and take it a step further… They realised that even if everything happens on the main thread, when you do observeOn(mainScheduler) RxJava actually submits a new task to the end of the execution loop of the main thread. If you are doing some fancy animation, the UI might jitter and that is really not what you want to have. Their solution was to remove the threading from the presenters altogether and leave it to the gateways. All the gateways need to return results on the main thread effectively meaning that the use cases and the presentation layer operate on the main thread. So, in this case, if the gateway does not do any background work hence does not require thread switching, all the code will run on the main thread without context switching due to RxJava’s scheduling implementation. So the DataProvider from before would look like this:
There are a lot of questions that came out of the discussion on how to implement Clean Architecture on Android. Which presentation pattern to use? how to implement the threading strategy? what should be the interface of your use cases? how that interface will look like when you add RxJava into the mix? Do you really need this extra abstraction of use cases? why not just call the gateways from the presenters themselves?
I tried to discuss the first two questions in this post and the one before that. I mean to answer the others in future posts. Hope you found this interesting and helpful.