Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why should I not use a message bus instead of a Loaders and Services?

In typical Android project where we need to pull data from somewhere (REST, SQL, cache, etc) into the UI in a clean way we commonly use a Loader, Service or (possibly, yuk) an AsyncTask, but I find all these approaches unsatisfactory for several reasons:

  • They're ugly, especially Loaders which have an appalling API structure
  • It's too easy to get wrapped up in threads, and tread on the UI thread
  • Our presentation layer code is being polluted with Android code and boilerplate. We're often passing Android objects (eg Cursors) right up into to the UI layer, which makes a clean architecture almost impossible to achieve. This forces us to mix business domain specific code (ideally plain Java objects) with Android platform code - not great for readability, maintenance, testing, or flexibility for future development. In practice we often get huge, messy Activity/Fragment classes.

I'm attracted by ideas such as those outlined in these articles: http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/ http://antonioleiva.com/mvp-android/ http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

Having successfully started using MVP to break the Activities/Fragments/Views into smaller/cleaner pieces, I'm now thinking that a solution to the problems above might be to rely on a message bus (Otto, EventBus, etc) instead of services or loaders or whatever to interact with the domain data.

So in practice that would mean instead of using (for example) a CursorLoader to load a Cursor from the database, I'd instead use the message bus to send a message to request data, the data is loaded on a background thread in response to that message, and then handle the response data when it arrives via a message on the UI thread. Crucially for me I'd prefer that data structure to be cast out of the business domain, not the Android domain, so I'd prefer an array of business objects, to a Cursor.

This being engineering there are always trade-offs, and although this seems to provide a much cleaner separation of concerns, there's less loader/service boilerplate, what are the downsides?

  • It may be harder (especially for new developers) to understand the code
  • It will be necessary to ensure the messages are sent and received on the right threads (Otto seems like it may have limitations here)
  • It will be necessary to avoid the temptation to implement everything as a message, which would ultimately be counterproductive
  • Passing collections of business objects around may be less efficient than using objects like Cursors. Though in many scenarios is it a problem in practice?
  • I do not know if Otto/EventBus are designed only to pass very small messages, or whether it is appropriate to pass larger objects (eg an array of business objects).

My question is are there any fundamental reasons NOT to take this message-based approach with Android apps?

like image 779
Ollie C Avatar asked Feb 20 '15 12:02

Ollie C


People also ask

Why should I use a message bus?

The primary benefit of a message bus architecture is that data is freely available. Services just provide data and don't mandate how it is used. You still have the necessary coordination in developing a system; part a generates messages like this and part b will do that.

What is a messaging bus?

A Message Bus is a combination of a common data model, a common command set, and a messaging infrastructure to allow different systems to communicate through a shared set of interfaces.


2 Answers

any fundamental reasons NOT to take this message-based approach with Android apps

There is no, I guess. In my practice I worked with 'stock' stuff like Loaders and AsyncTasks (I would not put Services in this row, because its responsibility is much broader). Then bus thing was implemented, and guess what? Life became easier and more predictable, decoupling increased. Once I totally moved to Rx, work became not only easier, but funnier! However, nothing will save you from dealing with lifecycles.

All this is just implementation details, it's more important to keep global things clear. When you talk about Clean Architecture, the thing is to hide from the UI layer (Activities, Fragments, Views..) how and where the objects come. Once you incapsulate this certain job in usecases, it's not a big deal which tool to use: loaders, buses or Rx - your UI layer should only stick to the interface provided by usecase - callbacks, events or observables

I'd point two things:

  1. The less any layer knows about the usecase implementation the better.
  2. If you've chosen one particular tool for implementation, use it everywhere. Do not mix several tools for the same job.
like image 104
Dmitry Gryazin Avatar answered Oct 06 '22 17:10

Dmitry Gryazin


They have provided explanations for their own resistance to such approaches:

1. Be careful with code abstractions.

2. Avoid dependency injection frameworks.

3. Avoid Creating Unnecessary Objects.

4. Avoid Internal Getters/Setters.

like image 30
Y.S Avatar answered Oct 06 '22 17:10

Y.S