Publishing and receiving events with Otto Android

    Otto is an Open Source project designed to provide an event bus implementation so that components can publish and subscribe to events.
    The library is forked from Google’s Guava, which has an EventBus class; Square’s take on the EventBus is simply called Bus, and is optimized for Android. Quoted from the official documentation:
"Unlike the Guava event bus, Otto will not traverse the class hierarchy and add methods from base classes or interfaces that are annotated. This is an explicit design decision to improve performance of the library as well as keep your code simple and unambiguous".
    Otto is a great way to communicate between your activity and fragments or to communicate between an activity and a service. In this post, I will present a simple project to "listening" the network state changed by BroadcastReceiver and update to UI thread through Otto instance.
Additional Information
This post is part of a series called Android TOP useful libraries

Importing library

    In order to use Otto, please add it's dependency to your app/build.gradle or download jar file from Otto official page:
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.squareup:otto:1.3.8' //otto dependency
}

Setup Otto in code

    When using Otto, you should make it available in the whole project. So, let make a subclass of Application and declaring an Otto instance like this:
MyApplication.java
package info.devexchanges.otto;

import android.app.Application;

import com.squareup.otto.Bus;
import com.squareup.otto.ThreadEnforcer;

public class MyApplication extends Application {

    private static Bus eventBus;

    public static Bus getInstance() {
       return eventBus;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        eventBus = new Bus(ThreadEnforcer.ANY);
    }
}
    As you can see at onCreate(), the Otto instance will be created after call:
eventBus = new Bus(ThreadEnforcer.ANY);
    In this example the ThreadEnforcer.ANY parameter is used, in this case Otto enforces that events will be able to send events from any thread. If you want to send events only from the main thread, use the ThreadEnforcer.MAIN parameter instead.

Sending events with Otto

    Now, making a BroadcastReceiver to detect the network state changed. In this class, sending event through Otto by post() method:
InternetBroadcastReceiver.java
package info.devexchanges.otto;

import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;

import java.text.SimpleDateFormat;
import java.util.Calendar;

public class InternetBroadcastReceiver extends BroadcastReceiver {

    @SuppressLint("SimpleDateFormat")
    @Override
    public void onReceive(Context context, Intent intent) {
        String status;
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        if (activeNetwork == null) {
            status =  "DISCONNECTED";
        } else if (activeNetwork.isConnected()) {
            status = "CONNECTED";
        } else if (activeNetwork.isConnectedOrConnecting()) {
            status = "CONNECTING";
        } else status = "";

        Log.d("Broadcast", "status: " + status);

        // Get current time
        Calendar c = Calendar.getInstance();
        SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yyyy:HH:mm:ss");
        String formattedDate = df.format(c.getTime());

        String eventData = "@" + formattedDate + ": device network state: " + status;

        // Post the event with this line
        MyApplication.getInstance().post(new TransferData(eventData));
    }
}

Register and unregister for events

    This process is usually in UI thread, mean in your Activities or Fragments. Event registration is done via the @Subcribe annotation on a public single parameter method. The method parameter is the event key, i.e., if such an data type is send via the Otto event bus the method is called.
Event receivers must register via the register() method of the Bus class.
    In this sample project, receiving Activity code simple like this:
MainActivity.java
package info.devexchanges.otto;

import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;

import com.squareup.otto.Subscribe;

public class MainActivity extends AppCompatActivity {

    private BroadcastReceiver broadcastReceiver;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text_view);

        MyApplication.getInstance().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();

        unregisterReceiver(broadcastReceiver);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        MyApplication.getInstance().unregister(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        broadcastReceiver = new InternetBroadcastReceiver();
        registerReceiver(broadcastReceiver, new IntentFilter());
    }

    @SuppressLint("SetTextI18n")
    @Subscribe
    public void getMessage(TransferData data) {
        Log.i("Activity", data.getMessage());
        textView.setText(textView.getText().toString() + "\n" + data.getMessage());
    }
}
    NOTE: Never forget to registering your BroadcastReceiver in AndroidManifest.xml and provide checking network state permission for your app:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.devexchanges.otto">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:allowBackup="true"
        android:name=".MyApplication"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name=".InternetBroadcastReceiver">
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
        </receiver>
    </application> 

</manifest>

Recommended transferring data type

    It is possible to send and receive events with a String or an Integer parameter, I recommend creating your own classes even if the only variable is a primitive. This way your code will be much cleaner, and keep in mind that events are identified by their parameters, so for example if you want to have two events with a String parameter and two different subscribers, both will receive the event if a producer fires. For example, this is the Object use for this project:
TransferData.java
package info.devexchanges.otto;

public class TransferData {
    private String message;

    public TransferData(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}
    After running app, you will have result like this (when your connection state is changed):

Final thoughts

    I also had a post which presented a similar library called EventBus, you can take a glance to compare which one is better. Moreover, you can go to it's Home Page to read the official doc for more details (like using @Produce annotation, configuation in proguard-project.txt file...). Finally, you can take full code of my project on @Github.


Share


Previous post
« Prev Post
Next post
Next Post »