time

Android BroadcastReceiver tutorial

A Broadcast Receiver (part of the android.content package) is a publish-and-subscribe system that is based on an Intent. In a sense, the purpose of an Android BroadcastReceiver is to "listen" to Android Intents.

Introduction

Broadcast receivers simply respond to broadcast messages from other applications or from the system itself. These messages are sometime called events or intents. When any of these events occur it brings the application into action by either creating a status bar notification or performing a task. Unlike activities, a BroadcastReceiver doesn’t contain any user interface. It’s generally implemented to do minimal amount of work, for example, delegate the tasks to services depending on the type of intent data that’s received. Following are some of the important system wide generated intents.

  • android.intent.action.BATTERY_CHANGED - sticky broadcast containing the charging state, level, and other information about the battery.
  • android.intent.action.BATTERY_LOW - indicates low battery condition on the device.
  • android.intent.action.BOOT_COMPLETED - this is broadcast once, after the system has finished booting.
  • android.intent.action.CALL - to perform a call to someone specified by the data.
  • android.intent.action.DATE_CHANGED - the date has changed.
  • android.intent.action.REBOOT - have the device reboot.
  • android.net.conn.CONNECTIVITY_CHANGE : The mobile network or wifi connection is changed(or reset)

Life cycle of a broadcast receiver is very simple: after the onReceive() of the receiver class has finished, the Android system is allowed to recycle the receiver. A BroadcastReceiver which is on the manifest is always active and it works even if the activity is no longer running. Receivers which are defined and registered dinamically only work when the app is running (such as LocalBroadcastManager).

Android BroadcastReceiver tutorial

Every intent you send as a broadcast must have an action name. Although the action name can be any string, I recommend that you always prefix it with the package name of your app in order to avoid conflicts with broadcasts of other applications.

The follow code snippet shows you how to create a new intent whose action name is me.proft.helloworld.broadcast

Intent i = new Intent("me.proft.helloworld.broadcast");
// i.putExtra("name", "value");

To send the intent as a broadcast, all you need to do is call the sendBroadcast() method and pass the Intent object as an argument to it.

sendBroadcast(i);

Note that the sendBroadcast() method creates a global broadcast that can be received by not only your application but also any other application installed on the user's device.

There are several types of Broadcast: Local, Normal, Ordered and Sticky.

Normal Broadcast

  • use sendBroadcast()
  • asynchronous broadcast
  • any receiver receives broadcast not any particular order

Ordered Broadcast

  • use sendOrderedBroadcast()
  • synchronous broadcast
  • receiver receives broadcast in priority base
  • we can also simply abort broadcast in this type

Local Broadcast

  • use only when broadcast is used only inside same process

Sticky Broadcast

  • normal broadcast intent is not available any more after is was send and processed by the system
  • use sendStickyBroadcast(Intent)
  • the corresponding intent is sticky, meaning the intent you are sending stays around after the broadcast is complete
  • because of this others can quickly retrieve that data through the return value of registerReceiver(BroadcastReceiver, IntentFilter)
  • apart from this same as sendBroadcast(Intent)

To set up a broadcast receiver in our android application we need to do the following two things.

  • Creating a BroadcastReceiver
  • Registering a BroadcastReceiver

A broadcast receiver is implemented as a subclass of BroadcastReceiver class and overriding the onReceive() method where each message is received as a Intent object parameter. Let’s quickly create a custom BroadcastReceiver as shown below:

public class MyReceiver extends BroadcastReceiver {
    public MyReceiver() {}

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Action: " + intent.getAction(), Toast.LENGTH_SHORT).show();
    }
}

BroadcastReceiver is an abstract class with the onReceiver() method being abstract. The onReceiver() method is first called on the registered broadcast receivers when any event occurs. The intent object is passed with all the additional data. A Context object is also available and is used to start an activity or service using context.startActivity(myIntent); or context.startService(myService); respectively.

A BroadcastReceiver can be registered in two ways.

By defining it in the AndroidManifest.xml file as shown below.

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

Using intent filters we tell the system any intent that matches our subelements should get delivered to that specific broadcast receiver.

By defining it programmatically.

IntentFilter filter = new IntentFilter();
filter.addAction(getPackageName() + "android.net.conn.CONNECTIVITY_CHANGE");

MyReceiver myReceiver = new MyReceiver();
registerReceiver(myReceiver, filter);

To unregister a receiver in onStop() or onPause() of the activity the following snippet can be used.

@Override
protected void onPause() {
    unregisterReceiver(myReceiver);
    super.onPause();
}

If you want your application itself should generate and send custom intents then you will have to create and send those intents by using the sendBroadcast() method inside your activity class. The following snippet is used to send an intent to all the related broadcast receivers.

Intent intent = new Intent();
intent.setAction("me.proft.helloworld.broadcast");
sendBroadcast(intent);

Don’t forget to add the above action in the intent-filter tag of the manifest or programmatically.

Sending broadcast from adb command line tool

You can use the following command from the adb command line tool. The class name and package names which are targeted via the command line tool need to be as defined in the manifest. You should send the intent you generated to your specific component, for example if you send a general ACTION_BOOT_COMPLETED broadcast, this will trigger a lot of things in an Android system.

# trigger a broadcast and deliver it to a component
adb shell am activity/service/broadcast -a ACTION -c CATEGORY -n NAME

# for example (this goes into one line)

adb shell am broadcast -a
android.intent.action.BOOT_COMPLETED -c android.intent.category.HOME -n
package_name/class_name

Example of BroadcastReceiver

Let’s develop an application that listens to network change events and also to a custom intent and handles the data accordingly.

The activity_main.xml consists of a button at the center that sends a broadcast intent.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:text="Send broadcast"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" 
        android:onClick="sendTestBroadcast" />
</RelativeLayout>

The MainActivity.java is given below.

public class MainActivity extends AppCompatActivity {
    ConnectionReceiver receiver;
    IntentFilter intentFilter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        receiver = new ConnectionReceiver();
        intentFilter = new IntentFilter("me.proft.helloworld.broadcast");
    }

    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(receiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(receiver);
    }

    void sendTestBroadcast(View v) {
        Intent intent = new Intent("me.proft.helloworld.broadcast");
        intent.putExtra("Foo", "Bar");
        sendBroadcast(intent);
    }
}

In the above code we’ve registered another custom action programmatically. The ConnectionReceiver is defined in the AndroidManifest.xml file as below:

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

The ConnectionReceiver.java class is defined below.

public class ConnectionReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("TAG", "" + intent.getAction());

        if(intent.getAction().equals("me.proft.helloworld.broadcast"))
            Toast.makeText(context, "HELLOWORLD is received", Toast.LENGTH_LONG).show();
        else {
            ConnectivityManager cm =
                    (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

            NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
            boolean isConnected = activeNetwork != null &&
                    activeNetwork.isConnectedOrConnecting();

            if (isConnected) {
                try {
                    Toast.makeText(context, "Network is connected", Toast.LENGTH_LONG).show();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                Toast.makeText(context, "Network is changed or reconnected", Toast.LENGTH_LONG).show();
            }
        }
    }
}

Example of LocalBroadcastManager

The LocalBroadcastManager class is a helper class to register local broadcast receivers as well as send broadcasts of intents to them within our own app’s process rather than sending global broadcasts using sendBroadcast() as we saw before. It is more efficient as well as secured compared to global broadcasts through the system as the data won’t leave our app nor can we receive from other foreign apps. Let’s see a really simple example of how to use it:

// generally in your onResume()
LocalBroadcastManager.getInstance(this).registerReceiver(new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String message = intent.getStringExtra("foo");
        Log.d("TAG", "foo : " + message);
    }
}, new IntentFilter("LOCAL-HELLOWORLD"));

We saw how to register, now here’s how to send an intent to it:

// send
Intent intent = new Intent("LOCAL-HELLOWORLD");
intent.putExtra("foo", "bar");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

Based on the action set which is LOCAL-HELLOWORLD, it’ll send the intent to our registered receiver to which we had passed an IntentFilter with the same action name.

We shouldn’t forget to unregister the receiver actually. So if we’re saving the receiver in an instance member called myReceiver then here’s how we should unregister it in onPause():

@Override
protected void onPause() {
    LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver);
    super.onPause();
}

Useful links

comments powered by Disqus