-
Notifications
You must be signed in to change notification settings - Fork 6.3k
Communicating with an Event Bus
By now, you should be familiar with the various ways the Android framework relies on passing data between the various abstractions used:
- Activities uses Intents to pass data between screens.
- Fragments use multiple ways to communicate: Bundles to construct fragments, methods by the containing Activity to call, and the listener pattern for the Fragment to fire events.
- Services rely on Broadcast Managers to send data to Applications.
One of primary issues of these current approaches is that they can create strong dependencies between each component, making it difficult to change one part of the system without impacting another area. This blog post describes the challenges of creating unmanageable dependencies with the current Android framework. Instead of encouraging more modular designs, the communication patterns can sometimes do the exact opposite.
Publish/subscribe models try to avoid this tight integration by relying an event bus model. In this type of model, there are publishers and subscribers. Publishers are responsible for posting events in response to some type of state change, while subscribers respond to these events. The event acts as an intermediary for exchanging information, isolating and minimizing the dependencies between each side. In this way, message buses create a communication pipeline that is intended to help make your app more maintainable and scalable.
One additional benefit of using these pub/sub frameworks is that they help facilitate passing Java objects between Activities, Fragments, or Services. You don't need to spend time serializing and deserializing data, which can often creates the tight bindings between these components. It also helps enforce more type-safety across complex Java objects.
There are many different libraries which attempt to enable the event bus model, including EventBus, RxJava, and Otto. Otto is by far the easiest to start using immediately. EventBus has a few more advanced features than in Otto described in this comparison chart, and RxJava enables a way to declare sequences of actions to be taken in response to certain events.
Event buses are especially helpful include notifying activities or fragments when tasks are completed, such as when a AsyncTask or a Background Service finishes. When using an event bus model, there are several considerations:
-
Don't assume you should replace every communication pattern with an event bus model. If you publish an event meant only for one subscriber that shouldn't trigger changes in other subscribers, rely on 1-to-1 communication patterns such as the Intents or listener pattern. For instance, a date picker fragment that could be reused in multiple components of your app should probably expose a listener interface since using a event bus model could cause this event to be published to many components waiting to respond to changes in the date selection. Consider the design patterns described in this article too.
-
If you are using publishing or subscribers within an Activity of Fragment they should be registered and unregistered with their lifecycle. Otherwise, it's likely you will encounter memory leaks or dangling references that can cause your app to crash.
-
Be wary about publishing events between Fragments. Events cannot be published or received when a Fragment is not running. If you have a Fragment publishing a message to another Fragment that isn't currently executing and then swap one for the other, it's likely the event will not be processed correctly. The EventBus library has a way to replay this event, but the Otto framework does not.
Add this Gradle dependency to your app/build.gradle
file:
dependencies {
compile 'com.squareup:otto:1.3.8'
}
One primary use case that the event bus model works well is replacing it lieu of the LocalBroadcastManager when communicating from a service to the application. One advantage is that you can simplify a lot of code that normally is used to serialize or deserialize the data by avoiding the need to pass Intents.
The first step we need to do is create a Singleton bus class. This shared bus will be used by your application to publish and subscribe to events.
public final class BusProvider {
private static final Bus BUS = new Bus();
public static Bus getInstance() {
return BUS;
}
private BusProvider() {
// No instances.
}
}
Since services normally run on separate threads from the main UI thread, we must also ensure that the events are published on the main thread by subscribers to handle especially if they update the screen. We expose some extra functionality to provide the ability for services to explicitly publish on the main thread:
private static final Handler mainThread = new Handler(Looper.getMainLooper());
public static void postOnMain(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
getInstance().post(event);
} else {
mainThread.post(new Runnable() {
@Override
public void run() {
getInstance().post(event);
}
});
}
}
If you wish to subscribe Activities and Fragments to the event bus, it should be done with respect to the lifecycle of these objects. The reason is that when an event is published, Otto will try to find all the registered subscribers. If there is a subscriber attached to the Activity currently not running, there is likely to be a stale reference that causes your app to crash.
public class MainActivity extends AppCompatActivity {
@Override
protected void onPause() {
super.onPause();
BusProvider.getInstance().unregister(this);
}
@Override
protected void onResume() {
super.onResume();
BusProvider.getInstance().register(this);
}
Create a new Event from a standard Java class. You can define any set of members variables. For now, we create a class to accept a result code and a String values.
public class IntentServiceResult {
int mResult;
String mResultValue;
IntentServiceResult(int resultCode, String resultValue) {
mResult = resultCode;
mResultValue = resultValue;
}
public int getResult() {
return mResult;
}
public String getResultValue() {
return mResultValue;
}
}
Inside our Intent service, we can should publish the event. We will simply pass an OK
result with
a message that will be displayed when the event is received on the Activity.
@Override
protected void onHandleIntent(Intent intent) {
// do some work
BusProvider.postOnMain(new IntentServiceResult(Activity.RESULT_OK, "done!!"));
}
We simply need to annotate a method that has this specific event type with the @Subscribe
decorator. The method must also take in the parameter of the event for which it is listening. In this case, it's IntentServiceResult
.
Assuming the Activity is registered on the bus, Otto will look for any subscribers when this specific event is published. We can display the result value to verify that the data passed is correct.
public class MainActivity extends AppCompatActivity {
@Subscribe public void doThis(IntentServiceResult intentServiceResult) {
Toast.makeText(this, intentServiceResult.getResultValue(), Toast.LENGTH_SHORT).show();
}
The example shown uses a singleton instance and makes a lot of repetitive BusProvider.getInstance()
calls. You can also use the Dagger 2 library to help reduce these calls. If we want to support posting events on the main thread, we can extend the Otto Bus
and provide an EventBus
instance instead:
public class EventBus extends Bus {
private final Handler mainThread = new Handler(Looper.getMainLooper());
public EventBus() {
}
public void postOnMain(final Object event) {
if(Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
this.mainThread.post(new Runnable() {
public void run() {
AndroidBus.this.post(event);
}
});
}
}
}
We simply need to declare in our Dagger module a singleton that provides an Otto Bus
object in AppModule.java
file:
@Provides
@Singleton
EventBus provideBus() {
return new EventBus();
}
Install Jake Wharton's Otto plugin that helps you navigate which elements are dependent on each other.
- http://awalkingcity.com/blog/2013/02/26/productive-android-eventbus/
- https://github.com/kevintanhongann/EventBusSample/
- http://nerds.weddingpartyapp.com/tech/2014/12/24/implementing-an-event-bus-with-rxjava-rxbus/
- http://timnew.me/blog/2014/12/06/typical-eventbus-design-patterns/
- https://github.com/kaushikgopal/Android-RxJava/
- https://gist.github.com/staltz/868e7e9bc2a7b8c1f754/
- https://github.com/square/otto/tree/master/otto-sample/
- http://www.stevenmarkford.com/passing-objects-between-android-activities/
Created by CodePath with much help from the community. Contributed content licensed under cc-wiki with attribution required. You are free to remix and reuse, as long as you attribute and use a similar license.
Finding these guides helpful?
We need help from the broader community to improve these guides, add new topics and keep the topics up-to-date. See our contribution guidelines here and our topic issues list for great ways to help out.
Check these same guides through our standalone viewer for a better browsing experience and an improved search. Follow us on twitter @codepath for access to more useful Android development resources.