7.3: Broadcast Receivers

Contents:

Explicit intents are used to start specific, fully qualified activities, as well as to pass information between activities in your app. Implicit intents are used to start activities based on registered components that the system is aware of, for example general functionality.

In this lesson you learn about broadcast intents, which don't start activities but instead are delivered to broadcast receivers.

Broadcast intents

The intents you've seen up to now always resulted in an activity being launched, either a specific activity from your application or an activity from a different application that could fulfill the requested action. But sometimes an intent doesn't have a specific recipient, and sometimes you don't want an activity to be launched in response to an intent. For example, when your app receives a system intent indicating that the network state of a device has changed, you probably don't want to launch an activity, but you may want to disable some functionality of your app.

For this reason there's a third type of intent that can be delivered to any interested application: the broadcast intent. Although broadcast intents are defined the same way as implicit intents, each type of intent has important distinguishing characteristics:

  • Broadcast intents are delivered using sendBroadcast() or a related method, while other types of intents use startActivity() to start activities. When you broadcast an intent, you never find or start an activity. Likewise, there's no way for a broadcast receiver to see or capture intents used with startActivity().
  • A broadcast intent is a background operation that the user is not normally aware of. Starting an activity with an intent, on the other hand, is a foreground operation that modifies what the user is currently interacting with.

There are two types of broadcast intents, those delivered by the system (system broadcast intents), and those that your app delivers (custom broadcast intents).

System broadcast intents

The system delivers a system broadcast intent when a system event occurs that might interest your app. For example:

  • When the device boots, the system sends an ACTION_BOOT_COMPLETED system broadcast intent. This intent contains the constant value "android.intent.action.BOOT_COMPLETED".
  • When the device is connected to external power, the system sends ACTION_POWER_CONNECTED, which contains the constant value "android.intent.action.ACTION_POWER_CONNECTED". When the device is disconnected from external power, the system sends ACTION_POWER_DISCONNECTED.
  • When the device is low on memory, the system sends ACTION_DEVICE_STORAGE_LOW. This intent contains the constant value "android.intent.action.DEVICE_STORAGE_LOW".

ACTION_DEVICE_STORAGE_LOW is a sticky broadcast, which means that the broadcast value is held in a cache. If you need to know whether your broadcast receiver is processing a value that's in the cache (sticky) or a value that's being broadcast in the present moment, use isInitialStickyBroadcast().

For more about common system broadcasts, visit the Intent reference.

To receive system broadcast intents, you need to create a broadcast receiver.

Custom broadcast intents

Custom broadcast intents are broadcast intents that your application sends out. Use a custom broadcast intent when you want your app to take an action without launching an activity, for example when you want to let other apps know that data has been downloaded to the device and is available for them to use.

To create a custom broadcast intent, create a custom intent action. To deliver a custom broadcast to other apps, pass the intent to sendBroadcast(), sendOrderedBroadcast(), or sendStickyBroadcast(). (For details about these methods, see the Context reference documentation.)

For example, the following method creates an intent and broadcasts it to all interested broadcast receivers:

public void sendBroadcastIntent() {
   Intent intent = new Intent();
   intent.setAction("com.example.myproject.ACTION_SHOW_TOAST");
   sendBroadcast(intent);
}
Note: When you specify the action for the intent, use your unique package name (com.example.myproject in the example) to make sure your intent doesn't conflict with an intent that is broadcast from a different app or from the system.

Broadcast receivers

Broadcast intents aren't targeted at specific recipients. Instead, interested apps register a component to "listen" for these kind of intents. This listening component is called a broadcast receiver.

Use broadcast receivers to respond to messages that are broadcast from other apps or from the system. To create a broadcast receiver:

  1. Define a subclass of the BroadcastReceiver class and implement the onReceive() method.
  2. Register the broadcast receiver either dynamically in Java, or statically in your app's manifest file.

    As part of this step, use an intent filter to specify which kinds of broadcast intents you're interested in receiving.

These steps are described below.

Define a subclass of BroadcastReceiver

To create a broadcast receiver, define a subclass of the BroadcastReceiver class. This subclass is where intents are delivered (if they match the intent filters you set when you register the subclass, which happens in a later step).

Within your subclass definition:

  • Implement the required onReceive() method.
  • Include any other logic that your broadcast receiver needs.

Example: Create a broadcast receiver

In this example, the AlarmReceiver subclass of BroadcastReceiver shows a Toast message if the incoming broadcast intent has the action ACTION_SHOW_TOAST:

private class AlarmReceiver extends BroadcastReceiver {
   @Override
   public void onReceive(Context context, Intent intent) {
       if (intent.getAction().equals(ACTION_SHOW_TOAST)) {
            CharSequence text = "Broadcast Received!";
            int duration = Toast.LENGTH_SHORT;

            Toast toast = Toast.makeText(context, text, duration);
            toast.show();
       }
   }
}
Note: Don't use asynchronous operations in your onReceive() implementation, because once your code returns from onReceive(), the system considers the BroadcastReceiver object to be finished. If onReceive() were to start an asynchronous operation, the system would kill the BroadcastReceiver process before the asynchronous operation had a chance to complete.

If you need a long-running operation that doesn't require a UI, use a Service launched from the broadcast receiver. In particular:

  • You can't show a dialog from within a BroadcastReceiver. Instead, use the NotificationManager API.
  • You can't bind to a service from within a BroadcastReceiver. Instead, use Context.startService() to send a command to the service.

Registering your broadcast receiver and setting intent filters

There are two ways to register your broadcast receiver: statically in the manifest, or dynamically in your activity.

Static registration

To register your broadcast receiver statically, add a <receiver> element to your AndroidManifest.xml file. Within the <receiver> element:

  • Use the path to your BroadcastReceiver subclass as the android:name attribute.
  • To prevent other applications from sending broadcasts to your receiver, set the optional android:exported attribute to false. This is an important security guideline.
  • To specify the types of intents the component is listening for, use a nested <intent-filter> element.

Example: Static registration and intent filter for a custom broadcast intent

The following code snippet is an example of static registration for a broadcast receiver that listens for a custom broadcast intent with "ACTION_SHOW_TOAST" in the name of its action:

  • The receiver's name is the module name plus the name of the BroadcastReceiver subclass defined above (AlarmReceiver).
  • The receiver is not exported, meaning that no other applications can deliver broadcasts to this app.
  • The receiver includes an intent filter that checks whether incoming intents include an action named ACTION_SHOW_TOAST.
    <receiver
    android:name="com.example.myproject.AlarmReceiver"
    android:exported="false">
    <intent-filter>
       <action android:name="com.example.myproject.intent.action.ACTION_SHOW_TOAST"/>
    </intent-filter>
    </receiver>
    

Intent filters

When the system receives an implicit intent to start an activity, it searches for the best activity for the intent by comparing it to intent filters, based on three aspects:

  • Action: Does the action specified in the intent match one of the <action> names listed in the filter? In the example above, only intents with ACTION_SHOW_TOAST in the name of their action match the filter.
  • Data: Does the data in the intent match one of the <data> types that are listed in the filter?
  • Category: Does every category in the intent match a <category> that's named in the filter?

Example: Intent filter for a system broadcast intent

This example shows an intent filter for a receiver that listens for the device to finish booting:

<intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>

To learn more about using intent filters to select intents, see the Intent Resolution section of the intent guide.

If no intent filters are specified, the broadcast receiver can only be activated with an explicit broadcast intent that names the component by name. (This is similar to how you launch activities by their class name with explicit intents.)

If you use static registration for your broadcast receiver, the Android system creates a new process to run your broadcast receiver if no processes associated with your application are running. This means that the receiver will respond, even if your app is not running.

Dynamic registration and unregistration

You can also register a broadcast receiver dynamically, which ties its operation to the lifecycle of your activity. To register your receiver dynamically, call registerReceiver() and pass in the BroadcastReceiver object and an intent filter. For example:

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_SHOW_TOAST);

mReceiver = new AlarmReceiver();
registerReceiver(mReceiver, intentFilter);
Note: If you register your receiver to receive only local broadcasts, you must register it dynamically; static registration isn't an option.

You also need to unregister the receiver by calling unregisterReceiver() and passing in your BroadcastReceiver object. For example:

unregisterReceiver(mReceiver);

Where you call these methods depends on the desired lifecycle of your BroadcastReceiver object:

  • If the receiver is only needed when your activity is visible (for example, to disable a network function when the network is not available), then register the receiver in onResume(). Unregister the receiver in onPause().
  • You can also use the onStart()/onStop() or onCreate()/onDestoy() method pairs, if they are more appropriate for your use case.

Security guidelines

When you use broadcast intents and broadcast receivers, information is sent between applications, which creates security risks. To avoid these risks, you can use LocalBroadcastManager (described below), or you can follow these guidelines:

  • Make sure that the names of intent actions and other strings are in a namespace that you own, or else you may inadvertently conflict with other applications. The intent namespace is global.
  • When you use registerReceiver(), any application can send broadcasts to that registered receiver. To control who can send broadcasts to it, use the permissions described below.
  • When you register a broadcast receiver statically, any other application can send broadcasts to it, regardless of the filters you specify. To prevent others from sending to it, make it unavailable to them with android:exported="false".
  • When you use sendBroadcast() or related methods, any other application can receive your broadcasts. To control who can receive such broadcasts, use the permissions described below.

Either the sender or receiver of a broadcast can enforce access permissions:

  • To enforce a permission when sending a broadcast, supply a non-null permission argument to sendBroadcast().

    Only receivers who have been granted this permission (by requesting it with the <uses-permission> tag in their AndroidManifest.xml) can receive the broadcast.

  • To enforce a permission when receiving a broadcast, supply a non-null permission when registering your receiver either when calling registerReceiver() or in the static <receiver> tag in your AndroidManifest.xml.

    Only broadcasters who have been granted this permission (by requesting it with the <uses-permission> tag in their AndroidManifest.xml) will be able to send an intent to the receiver. The receiver has to request permission in the manifest, regardless of whether the sender is registered statically or dynamically.

LocalBroadcastManager

To avoid having to manage the security aspects described in the Security guidelines, use the LocalBroadcastManager class. LocalBroadcastManager lets you send and receive broadcasts within a single process and a single application, which means you don't have to worry about cross-application security.

Sending local broadcasts

To send a broadcast using LocalBroadcastManager:

  1. Get an instance of LocalBroadcastManager by calling getInstance() and passing in the application context.
  2. Call sendBroadcast() on the instance, passing in the intent that you want to broadcast.

For example:

LocalBroadcastManager.getInstance(this).sendBroadcast(customBroadcastIntent);

Registering your receiver for local broadcasts

To register your receiver to receive only local broadcasts:

  1. Get an instance of LocalBroadcastManager by calling getInstance() and passing in the application context.
  2. Call registerReceiver(), passing in the receiver and an intent filter as you would for a regular broadcast receiver. You must register local receivers dynamically, because static registration in the manifest is unavailable.

For example:

LocalBroadcastManager.getInstance(this).registerReceiver
       (mReceiver, new IntentFilter(CustomReceiver.ACTION_CUSTOM_BROADCAST));

To unregister the broadcast receiver:

LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);

The related exercises and practical documentation is in Android Developer Fundamentals: Practicals.

Learn more

results matching ""

    No results matching ""