Buses appeared on Android some years ago with Otto, forked from an intern component of Guava, one of the Google core libraries. After Otto, arrived EventBus, a more complete solution permitting, among others, inheritance in subscriber classes, delivery threads and priority on receiving.

Recently Otto has been deprecated in favor of RxJava, a more advanced library, making also possible a communication by event. Buses stay simple ways to answer to a certain kind of problems met on Android.

Buses permit the application of the Publisher/Subscriber model (PubSub).

In software architecture, publish–subscribe is a messaging pattern where senders of messages, called publishers, do not program the messages to be sent directly to specific receivers, called subscribers, but instead characterize published messages into classes without knowledge of which subscribers, if any, there may be. Similarly, subscribers express interest in one or more classes and only receive messages that are of interest, without knowledge of which publishers, if any, there are.

EventBus setup

Publication :

EventBus.getDefault().post(new MessageEvent("Hello !")); // Simple Event Class (POJO)

public class MessageEvent {

public final String message; public MessageEvent(String message) {

this.message = message;

}

}

Subscribe / unsubscribe to the Bus :

// Typically an Activity or a Fragment

@Override

public void onStart() {

super.onStart();

EventBus.getDefault().register(this);

} @Override

public void onStop() {

EventBus.getDefault().unregister(this);

super.onStop();

}

Reading :

// Reading of events of type MessageEvent (free method name)

@Subscribe

public void onMessageEvent(MessageEvent event){

Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();

}

When to use a bus ?

When a certain number of problems occur :

communications between entities not directly linked (propagation of a message through different layers, eg : listener of listener)

many listeners

1-X communications

When not to use a bus ?

long chain of events (prefer RxAndroid)

inter-process communication (use bounded services)

Gain

less boilerplate code

lower coupling -> better maintenability

1-X communications easier to setup

asynchronous task : error management, guaranteed delivery

easier to test (mock of message vs mock of activity)

Limits

less structuring (no interface implementation to link the components)

potentially confusing : be careful with readers which resend messages

additional abstraction

Advices

do not use for all communications (eg : not for direct communications)

avoid the resend of message from a subscriber method (even forbid it)

I will now detail the analysis by presenting the advantages of the use of a bus against the different native ways of communication.

Comparison with the native ways of communication

vs Communication by interface :

(eg : listener of Fragment)

pros : less boilerplate code : interfaces (declarations and implementation), listener management (declaration, assignment and reattachment after recreate), direct communication between different layers

cons : less structuring, creation of message objects

// Class consuming the interaction

public class ExempleActivity extends Activity implements ExempleFragment.OnFragmentInteractionListener {

... @Override

public void onFragmentInteraction(Uri uri) {

// Consumption of the interaction

}

} // Class producing the interaction

public class ExempleFragment extends Fragment { private OnFragmentInteractionListener mListener;

... public void onButtonPressed(Uri uri) {

if (mListener != null) {

// Production of the interaction

mListener.onFragmentInteraction(uri);

}

} // Attachment of the listener

@Override

public void onAttach(Context context) {

super.onAttach(context);

if (context instanceof OnFragmentInteractionListener) {

mListener = (OnFragmentInteractionListener) context;

} else {

throw new RuntimeException(context.toString()

+ " must implement OnFragmentInteractionListener");

}

} // Detachment of the listener

@Override

public void onDetach() {

super.onDetach();

mListener = null;

} // Declaration of the interaction method

public interface OnFragmentInteractionListener {

void onFragmentInteraction(Uri uri);

}

}

vs Communication by Intent :

(eg : LocalBroadcastManager, passing of Intent through multiple layers, asynctask callback following an orientation change)

pros : type safe (vs extras), no serialization (vs parcelable), simpler API than LocalBroadcastManager

public class ExampleActivity extends AppCompatActivity {

...

// Declaration of the broadcast receiver

private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) {

// Retrieving of the parcelable object

ComplexObject complexObject =

intent.getParcelableExtra("MyComplexObject");

textView.setText(complexObject.getMessage());

}

}; @Override

protected void onCreate(Bundle savedInstanceState) {

...

AsyncTask asyncTask = new ExampleTask();

asyncTask.execute("Hello ! ");

...

} @Override

public void onResume() {

super.onResume();

LocalBroadcastManager localBroadcastManager =

LocalBroadcastManager.getInstance(this);

// Registering of the broadcast receiver

localBroadcastManager.registerReceiver(mMessageReceiver,

new IntentFilter("MyIntentMessage"));

} @Override

public void onPause() {

// Unregistering of the broadcast receiver

localBroadcastManager.unregisterReceiver(mMessageReceiver);

super.onPause();

} // Declaration of the message object

// Implementation of the parcelable methods

class ComplexObject implements Parcelable { private String message;

// Other fields ... public ComplexObject(String message) {

this.message = message;

} public String getMessage() {

return message;

} protected ComplexObject(Parcel in) {

message = in.readString();

} public final Creator<ComplexObject> CREATOR =

new Creator<ComplexObject>() {

@Override

public ComplexObject createFromParcel(Parcel in) {

return new ComplexObject(in);

} @Override

public ComplexObject[] newArray(int size) {

return new ComplexObject[size];

}

}; @Override

public void writeToParcel(Parcel parcel, int i) {

parcel.writeString(message);

// Writting of the other fields...

} @Override

public int describeContents() {

return 0;

}

} class ExampleTask extends AsyncTask<Object, Void, Void>{ @Override

protected Void doInBackground(Object... objects) {

Intent intent = new Intent("MyIntentMessage");

intent.putExtra("MyComplexObject",

new ComplexObject((String)strings[0]));

LocalBroadcastManager.getInstance(getApplicationContext()).

sendBroadcast(intent); return null;

} }

}

vs Communication by Handler/Message from a background thread :

(ex. : threads, services, etc.)

pros : simpler, error management with AsyncExecutor (EventBus)

public class ReceiveFromServiceActivity extends AppCompatActivity { // Message handler of the service

class ResponseHandler extends Handler {

@Override public void handleMessage(Message message) {

Toast.makeText(ReceiveFromServiceActivity.this, "message from service : " + message.getData().getString("message"),

Toast.LENGTH_SHORT).show();

}

}

// Messenger toward the activity

Messenger messenger = new Messenger(new ResponseHandler()); @Override

protected void onCreate(Bundle savedInstanceState) {

...

Intent intent = new Intent(this, SendingService.class);

// Passing of the messenger to the service

intent.putExtra("activityMessenger", messenger);

startService(intent);

} } public class SendingService extends Service { // Messenger toward the activity

private Messenger activityMessenger; @Override

public void onStart(Intent intent, int startId) {

// Retrieving of the messenger toward the activity

activityMessenger = intent.getParcelableExtra("activityMessenger");

// Send of a message to the activity

Message messageToActivity = Message.obtain();

messageToActivity.getData().putString("message", "Hello activity !");

try {

activityMessenger.send(messageToActivity);

} catch (RemoteException e) {

e.printStackTrace();

}

}

}

vs Observer Model :

(eg : Observer and ContentObserver)

pros : simpler, less structuring code (declarations)

cons : change of vision (message vs state changes)

public class ObserverActivity extends Activity implements Observer{

...

@Override

protected void onCreate(Bundle savedInstanceState) {

...

MyObservable myObservable = new MyObservable();

// Registering of the observer (here the activity)

myObservable.addObserver(this);

// Changing the state of the observable

myObservable.setValue("Hello ! ");

} // Update of the observer on a state change notification

@Override

public void update(Observable observable, Object data) {

textView.setText(((MyObservable)observable).getValue());

} class MyObservable extends Observable { private String mValue; public String getValue() {

return mValue;

} public void setValue(String mValue) {

this.mValue = mValue;

// Mark the change of state

setChanged();

// Notification of the change for update

notifyObservers();

}

}

}

Conclusion

The support of the historical Otto is stopped for RxAndroid, do we then have to migrate our buses to Rx ? In my opinion No.

Rx is more than a bus solution and brings a new paradigm (oriented toward functional programming), so more complex to setup. It is also more centered on the management of sequences of producer/consumer task.

Buses, especially, the more complete and optimized EventBus, have still its place on Android for simpler problems.

References :

http://fr.slideshare.net/greenrobot/eventbus-for-android-15314813

https://guides.codepath.com/android/Communicating-with-an-Event-Bus

http://timnew.me/blog/2014/12/06/typical-eventbus-design-patterns/

http://endlesswhileloop.com/blog/2015/06/11/stop-using-event-buses/

http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

Android Developer at jacquesgiraudel.com