7.3: Broadcast Receivers

Contents:

Certain events that can happen in the Android system might affect the functionality of applications installed on the device. For example, if the system has finished booting, you might like your weather app to update its information. The Android framework handles this by sending out system broadcasts containing Intents that are meant to be received using BroadcastReceivers. A BroadcastReceiver is the base class for code that will receive Intents sent by sendBroadcast(). There are two major classes of broadcasts that can be received:

  • Normal broadcasts (sent with Context.sendBroadcast()) are completely asynchronous. All receivers of the broadcast are run in an undefined order, often at the same time. This is more efficient, but means that receivers cannot use the result or abort APIs included here.
  • Ordered broadcasts (sent with Context.sendOrderedBroadcast) are delivered to one receiver at a time. As each receiver executes in turn, it can propagate a result to the next receiver, or it can completely abort the broadcast so that it won't be passed to other receivers. The order receivers run in can be controlled with the android:priority attribute of the matching intent-filter; receivers with the same priority will be run in an arbitrary order.

Even in the case of normal broadcasts, the system may in some situations revert to delivering the broadcast one receiver at a time. In particular, for receivers that may require the creation of a process, only one will be run at a time to avoid overloading the system with new processes. In this situation, however, the non-ordered semantics hold: these receivers still cannot return results or abort their broadcast.

Additionally, you can create Intents with custom actions and broadcast them yourself from your application using thesendBroadcast() method. The broadcast will be received by all applications with a BroadcastReceiver registered for that action. To learn more about broadcast Intents and Broadcast receivers, visit the Intent documentation.

It is useful to note that while the Intent class is used for sending and receiving broadcasts, the Intent broadcast mechanism is completely separate from Intents that are used to start Activities.

In this practical, you'll create an app that responds to a change in the charging state of your device, as well as sends and receives a custom Broadcast Intent.

What you should already KNOW

Prior to this practical, you should be able to:

  • Identify key parts of the AndroidManifest.xml file.
  • Create Implicit Intents.

What you will LEARN

During this practical, you will learn to:

  • Subclass and implement a BroadcastReceiver.
  • Register for system Broadcast intents.
  • Create and send custom Broadcast intents.

What you will DO

In this practical, you will:

  • Subclass a BroadcastReceiver to show a Toast when a broadcast is received.
  • Register your receiver to listen to system broadcasts.
  • Send and receive a custom broadcast intent.

App overview

The PowerReceiver application will register a BroadcastReceiver that displays a Toast message when the device is connected or disconnected from power. It will also send and receive a custom Broadcast Intent to display a different Toast message.

Preview of the Power Receiver App

Task 1. Set up the PowerReceiver Project

1.1 Create the Project

  1. Create a new project called PowerReceiver, accept the default options and use the Empty template.
  2. Create a new Broadcast Receiver. Select the package name in the Android Project View and navigate to File > New > Other > Broadcast Receiver.
  3. Name the class CustomReceiver and make sure "Exported" and "Enabled" are checked.

    Note: The "Exported" feature allows your application to respond to outside broadcasts, while "Enabled" allows it to be instantiated by the system.
  4. Navigate to your Android manifest file. Note that Android Studio automatically generates a <receiver> tag with your chosen options as attributes. BroadcastReceivers can also be registered programmatically, but it is easiest to define them in the manifest.

1.2 Register your Receiver for system broadcasts

In order to receive any broadcasts, you must first determine which broadcast intents you are interested in. In the Intent documentation, under "Standard Broadcast Actions", you can find some of the common broadcast intents sent by the system. In this app, you will be interested in two particular broadcasts: ACTION_POWER_CONNECTED and ACTION_POWER_DISCONNECTED. BroadcastReceivers register for broadcast the same way you registered your activities for implicit Intents: you use an intent filter. You learned about implicit intents in an earlier practical.

  1. In the AndroidManifest.xml file, add the following code between the <receiver> tags to register your Receiver for the system Intents:
    <intent-filter>
       <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
       <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
    </intent-filter>
    

1.3 Implement onReceive() in your BroadcastReceiver

Once the BroadcastReceiver intercepts a broadcast that it is registered for, the Intent is delivered to the receiver's onReceive() method, along with the context in which the receiver is running.

  1. Navigate to your CustomReceiver file, and delete the default implementation inside the onReceive() method.
  2. Obtain the action from the intent and store it in a String variable called intentAction:
    @Override
    public void onReceive(Context context, Intent intent) {
       String intentAction = intent.getAction();
    }
    
  3. Create a switch statement with the intentAction string, so that your app can display a different toast message for each specific action your receiver is registered for:
    switch (intentAction){
       case Intent.ACTION_POWER_CONNECTED:
           break;
       case Intent.ACTION_POWER_DISCONNECTED:
           break;
    }
    
  4. Initialize a String variable called toastMessage before the switch statement, and make it's value null so that it can be set depending on the broadcast action you receive.
  5. Assign toastMessage to "Power connected!" if the action is ACTION_POWER_CONNECTED, and "Power disconnected!" if it is ACTION_POWER_DISCONNECTED. Extract your string resources.
  6. Display a toast message for a short duration after the switch statement:
    Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show();
    
  7. Run your app. After it is installed, unplug your device. It may take a moment the first time, but sure enough, a toast is displayed each time you plug in, or unplug your device.
    Note: If you are using an emulator, you can toggle the power connection state by selecting the ellipses icon for the menu, choose Battery on the left bar, and toggle using the Charger connection setting.

1.4 Restrict your Broadcast Receiver

Broadcast Receivers are always active, and therefore your app does not even need to be running for its onReceive() method to be called.

  1. Go ahead, try it out: close your app, and plug or unplug your device.

    The toast message is still displayed!

    There is a lot of responsibility on you, as the developer, to not overwhelm your user with notifications or unwanted functionality every time a broadcast occurs. In this example, having a Toast message pop up every time the power state changes could quickly annoy the user. To limit this, you will add some code to ensure that the broadcast receiver is only active when the app is showing.

    The PackageManager class is responsible for enabling and disabling a particular android component (such as a service, activity or broadcast receiver). This is accomplished using the setComponentEnabledSetting()method which takes three arguments:

    • The ComponentName (an identifier for the component you want to enable or disable).
    • One of the PackageManager class constants that represent the enabled state of a component. In this app we will use PackageManager.COMPONENT_ENABLED_STATE_ENABLED and PackageManager.COMPONENT_ENABLED_STATE_DISABLED. See the PackageManager reference for the other constants.
    • An optional flag constant that tells the system not to kill the app when changing the state of the component: PackageManager.DONT_KILL_APP.
  2. For the broadcast receiver to only be active when the app is showing, enable it in onStart() and disable it in onStop().

  3. Create two member variables: a PackageManager and a ComponentName.
  4. Initialize both of them in onCreate().

    Instantiate the PackageManager with getPackageManager(). The constructor for ComponentName takes the application context and the class name of the component:

    mReceiverComponentName = new ComponentName(this, CustomReceiver.class);
    mPackageManager = getPackageManager();
    
  5. Override both onStart() and onStop():
    @Override
    protected void onStart() {
       super.onStart();
    }
    @Override
    protected void onStop() {
       super.onStop();
    }
    
  6. Call setComponentEnabledSetting() on the PackageManager in onStart(). Pass in the Component name, the PackageManager.COMPONENT_ENABLED_STATE_ENABLED constant, and the DONT_KILL_APP flag:
    mPackageManager.setComponentEnabledSetting
    (mReceiverComponentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
    PackageManager.DONT_KILL_APP);
    
  7. In onStop(), use the PackageManager to disable the CustomReceiver, using the PackageManager.COMPONENT_ENABLED_STATE_DISABLED constant:
    mPackageManager.setComponentEnabledSetting
    (mReceiverComponentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
    PackageManager.DONT_KILL_APP);
    

Task 2. Send and Receive a Custom Broadcast

In addition to responding to system broadcasts, your application can also send and receive custom Broadcast Intents. A custom broadcast intent is exactly the same a system one except you must define your own Intent action for it (a unique string) and it's delivered using the sendBroadcast() method. In this task, you will add a button to your activity that sends a custom Broadcast Intent, which will be registered by your receiver and displayed in a Toast message.

2.1 Define your custom Broadcast Action string

Both the sender and receiver of a custom broadcast must agree on a unique action string for the Broadcast Intent. It is a common practice to create unique action strings by prepending your Action Name with your package name.

  1. Create a constant String variable in both your MainActivity and your CustomReceiver class to be used as the Broadcast Intent Action (this is your custom action string):
    private static final String ACTION_CUSTOM_BROADCAST =
    "com.example.android.powerreceiver.ACTION_CUSTOM_BROADCAST";
    

2.2 Add a "Send Custom Broadcast" Button

  1. In your activity_main.xml file, add a Button view with the following attributes:

    Attribute

    Value

    android:id

    "@+id/sendBroadcast"

    android:layout_width

    wrap_content

    android:layout_height

    wrap_content

    android:text

    "Send Custom Broadcast"

    android:layout_margin

    "8dp"

    android:onClick

    "sendCustomBroadcast"

  2. Extract your string resources.
  3. Create the stub for the sendCustomBroadcast() method: Click in the yellow highlighted onClick method name. Press Alt (Option for Mac users) + Enter and choose 'Create 'sendCustomBroadcast(View)' in 'MainActivity'.

2.3 Implement sendCustomBroadcast()

Because this broadcast is meant to be used solely by your application, you should use LocalBroadcastManager to manage the broadcasts in your application. LocalBroadcastManager is a class that allows you to register for and send broadcasts of Intents to local objects within your app. By keeping broadcasts local, your application data will not be shared with other Android applications, keeping your information more secure and maintaining system efficiency.

  1. In the sendCustomBroadcast() method in MainActivity, create a new Intent, with your custom action string as the argument.
    Intent customBroadcastIntent = new Intent(ACTION_CUSTOM_BROADCAST);
    
  2. Send the broadcast using the LocalBroadcastManager class: LocalBroadcastManager.getInstance(this).sendBroadcast(customBroadcastIntent);

2.4 Register your Custom Broadcast

For system broadcasts, you registered your receiver in the AndroidManifest.xml file. It is also possible to register your receiver for specific actions programmatically. For broadcasts sent using LocalBroadcastManager, static registrations in the manifest is not allowed.

If you programmatically register the broadcast receiver, you must also unregister the receiver when it is no longer needed. In your application, the receiver will only need to respond to the custom broadcast when it is running, so we can therefore register the action in onCreate() and unregister it in onDestroy().

  1. Create a member variable in MainActivity for your Receiver and initialize it:
    private CustomReceiver mReceiver = new CustomReceiver();
    
  2. In onCreate(), get an instance of LocalBroadcastManager and register your receiver with the custom intent action:
    LocalBroadcastManager.getInstance(this)
    .registerReceiver(mReceiver, new IntentFilter(ACTION_CUSTOM_BROADCAST));
    
  3. Override the onDestroy() method and unregister your receiver from the LocalBroadcastManager:
    @Override
    protected void onDestroy() {
       LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
       super.onDestroy();
    }
    

2.5 Respond to the Custom Broadcast

  1. In onReceive() in your CustomReceiver class, add a case statement for the custom Intent Action.
  2. Modify the toast message to "Custom Broadcast Received", extract it into your strings.xml and call it custom_broadcast_toast (press Alt + Enter or Option + Enter on a Mac and choose extract string resource):
    case ACTION_CUSTOM_BROADCAST:
       toastMessage = context.getString(R.string.custom_broadcast_toast);
       break;
    
    Note: Broadcast Receivers that are registered programmatically are not affected by the enabling or disabling done by the PackageManager class, which is meant for components listed in the Android Manifest file. Enabling or disabling such receivers is done by registering or unregistering them, respectively. In this case, turning off the "Receiver Enabled" toggle will stop the power connected or disconnected toast messages, but not the Custom Broadcast Intent Toast messages.

That's it! Your app now delivers custom Broadcast intents and is able to receive both system and custom Broadcasts.

Solution code

Android Studio project: PowerReceiver

Coding challenge

Note: All coding challenges are optional and are not prerequisites for later lessons.

Challenge: A common pattern for broadcast receivers is starting some update or action once the device has booted. Implement a Broadcast Receiver that will show a toast message half an hour after the device has booted.

Summary

  • Broadcast Receivers are one of the fundamental components of an android application.
  • Broadcast Receivers can receive Intents broadcasted by both the system and application.
  • The Intent broadcast mechanism is completely separate from Intents that are used to start Activities.
  • You need to subclass the BroadcastReceiver class and implement onReceive() to process the incoming Intent associated with the broadcast.
  • A broadcast receiver can be registered in the Android manifest file or programmatically.
  • Use LocalBroadcastManager to register and send for Broadcasts that are private to your application.
  • LocalBroadcastManager is more efficient and secure than system broadcasts.
  • For broadcasts sent using LocalBroadcastManager, you can only register interest for specific actions programmatically.
  • A common practice to create unique Intent action names for broadcasts is to prepend your Action Name with your package name.

The related concept documentation is in Android Developer Fundamentals: Concepts.

Learn more

Android Developer Documentation

Guides

Reference

results matching ""

    No results matching ""