Smarter ideas worth writing about.

Event Bus on Android

Background

We often need a mechanism to send and receive messages within different parts of our software systems. For example, a scroll view may need to tell a component that more data needs to be loaded and displayed. A background task needs to communicate it's finished downloading a file. We often turn to traditional communication methods such as Interfaces or directly calling an object’s function. These methods increase coupling between components and make unit testing more difficult. Consider using an Event Bus to publish and subscribe to users and system events within your application. An Event Bus is an alternate object communication system used to keep your system decoupled and flexible.    

Strive for Decoupled Code
A solid principle of good software design is decoupling between components. The main goal of decoupling is reducing the affects of change throughout a system. When a change is made in one module, a well-designed decoupled system will usually reduce the need to make changes within another module. We are going to see how the Otto Event Bus can be used to send messages in our Android systems. Otto is easy to setup and it removes a lot of boilerplate code, while providing a clean way to communicate between software components.

Many of Android’s application components are decoupled right “out-of-the-box” such as Activities, Fragments, Services, Content Providers, and Broadcast Receivers. To interact with these components, we use Intents which tell the Android system to start an Activity, Service, or activate a Broadcast Receiver. Intents are a strict means of communicating between these components. The Otto Event Bus allows us to send and receive event messages throughout all parts of our system without the restriction of Intents or direct object references. In addition, we often make use of interfaces for Fragment-to-Activity or Fragment-to-Fragment communication. Using an Event Bus can free us from the strict object coupling created by implementing interfaces in our components. 

Example Application
The sample app is a simple list of football team names displayed in a list fragment. You can clone the code from github here: https://github.com/CardinalNow/event-bus-example. The team name can be modified through a dialog fragment and the list is refreshed. The Otto framework handles loading the edit dialog fragment and refreshing the team name list fragment. The sample Android application does require Android Studio version 1.0.1.

Step 1: Get the bus started:
The first step is to add the Otto dependency to your app’s build.gradle file. This will tell the gradle build system to pull-in the Otto framework and make it available for use in your app.

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.1"

    defaultConfig {
        applicationId "com.cardinalsolutions.eventbus"
        minSdkVersion 15
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.squareup:otto:1.3.5'
}

For most applications, you will only need to use one Bus object throughout your application. In our sample app, we have the ‘FootballBus’ class that creates a single instance of the OttoBus() object. Once a new instance of the Bus() is created; we can start posting and subscribing to events throughout application.

To start listening for events add the following line of code in your Activity or Fragment’s onCreate() method. This will start the bus and make the current class a receiver of event messages. 


@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FootballBus.getInstance().register(this);
    }


Step 2: Post an Event:


A typical use case for lists is to load a detail or edit screen after the user taps a list item. We often utilize a callback interface between the ListFragment and Activity or call a method directly from the activity. Using Otto, we can post an Event to notify the Activity that we want to perform an action after the list item is tapped. The Activity will be subscribing to an event that our ListFragment will post. 

An Event is an object that is posted on the bus and will be delivered to any class subscribing to the same Event object. In our sample app, we have a ‘OnFootballTeamClickedEvent’ class which hold a reference to the team name that is tapped in our list view. If you are following along in your sample app, we override the ‘onListItemCick()’ method in the ListFragment and post a new ‘OnFootballTeamClickEvent’ event passing in the team name in the constructor.


    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        Bus bus = FootballBus.getInstance();
        bus.post(new OnFootballTeamCLickedEvent(teams.get(position)));
    }


Step 3: Subscribe to an Event:

Once you post an event, you expect it to be delivered somewhere within your application. In our sample app, Otto will notify the MainActivity() that the user wants to edit the selected football team’s name. In order to make Otto’s delivery system to work, a class must subscribe to one of your defined event class. To listen and subscribe to an event within a class, perform the following tasks.

    • Start listening for events by registering a class on the bus

    @Override
    protected voidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadFragment(savedInstanceState);
        FootballBus.getInstance().register(this);
    }

    • Decorat a method signature with the @Subscribe annotation and pass an event object as the method parameter bus


      @Subscribe
    public void onTeamClicked(OnFootballTeamCLickedEvent event){
        TeamDetailDialogFragment fragment = TeamDetailDialogFragment.newInstance(event.teamName);

        fragment.show(getFragmentManager(), "DIALOG_FRAGMENT");
    }

In our sample, the ‘OnFootballTeamClickedEvent’ is the parameter in the ‘onTeamClicked’ method, which is annotated with @Subscribe. The ‘MainListFragment’ posts the ‘OnFootballTeamClickedEvent’ on the bus and the ‘MainActivity’s’ ‘onTeamClicked’ method will be invoked to display the team’s detail dialog.

Wrap up
This simple application illustrates using Otto for Fragment-to-Activity communication. We did not implement any interfaces or invoke methods directly to display our edit dialog fragment. The Activity, ListFragment, and DialogFragment all stay decoupled from one another. While Fragment-to-Activity communication is one use-case for Otto, we can use it for Fragment-to-Fragment or IntentService-to-Activity communication. 



Share:

About The Author

Steven is a developer in Cardinal's Cincinnati Mobile Practice. He has experience with iOS, Android, as well as the Microsoft platform.