7.3 Broadcasts

Contents:

In this chapter you learn about broadcasts and broadcast receivers. Broadcasts are messaging components used for communicating across different apps, and also with the Android system, when an event of interest occurs. Broadcast receivers are the components in your Android app that listen for these events and respond accordingly.

Broadcasts

Broadcasts are messages that the Android system and Android apps send when events occur that might affect the functionality of other apps. For example, the Android system sends an event when the system boots up, when power is connected or disconnected, and when headphones are connected or disconnected. Your Android app can also broadcast events, for example when new data is downloaded.

In general, broadcasts are messaging components used for communicating across apps when events of interest occur. There are two types of broadcasts:

  • System broadcasts are delivered by the system.
  • Custom broadcasts are delivered by your app.

System broadcasts

A system broadcast is a message that the Android system sends when a system event occurs. System broadcasts are wrapped in Intent objects. The intent object's action field contains event details such as android.intent.action.HEADSET_PLUG, which is sent when a wired headset is connected or disconnected. The intent can also contain more data about the event in its extra field, for example a boolean extra indicating whether a headset is connected or disconnected.

Examples:

  • When the device boots, the system broadcasts a system Intent with the action ACTION_BOOT_COMPLETED.
  • When the device is disconnected from external power, the system sends a system Intent with the action field ACTION_POWER_DISCONNECTED.

System broadcasts aren't targeted at specific recipients. Interested apps must register a component to "listen" for these events. This listening component is called a broadcast receiver.

As the Android system evolves, significant changes are made to improve the system's performance. For example, starting from Android 7.0, the system broadcast actions ACTION_NEW_PICTURE and ACTION_NEW_VIDEO are not supported. Apps can no longer receive broadcasts about these actions, regardless of the targetSDK version on the device. This is because device cameras take pictures and record videos frequently, so sending a system broadcast every time one of these actions occurred would strain a device's memory and battery.

To get the complete list of broadcast actions that the system can send for a particular SDK version, check the broadcast_actions.txt file in your SDK folder, at the following path: Android/sdk/platforms/android-xx/data, where xx is the SDK version.

Custom broadcasts

Custom broadcasts are broadcasts that your app sends out. Use a custom broadcast when you want your app to take an action without launching an activity. For example, use a custom broadcast when you want to let other apps know that data has been downloaded to the device and is available for them to use. More than one broadcast receiver can be registered to receive your broadcast.

To create a custom broadcast, define a custom Intent action.

Note: When you specify the action for the Intent, use your unique package name (for example com.example.myproject) to make sure that your intent doesn't conflict with an intent that is broadcast from a different app or from the Android system.

There are three ways to deliver a custom broadcast:

Normal, ordered, and local broadcasts are described in more detail below.

Normal broadcasts

The sendBroadcast() method sends broadcasts to all the registered receivers at the same time, in an undefined order. This is called a normal broadcast. A normal broadcast is the most efficient way to send a broadcast. With normal broadcasts, receivers can't propagate the results among themselves, and they can't cancel the broadcast.

The following method sends a normal broadcast to all interested broadcast receivers:

public void sendBroadcast() {
   Intent intent = new Intent();
   intent.setAction("com.example.myproject.ACTION_SHOW_TOAST");
   // Set the optional additional information in extra field.
   intent.putExtra("data","This is a normal broadcast");
   sendBroadcast(intent);
}

Ordered broadcasts

To send a broadcast to one receiver at a time, use the sendOrderedBroadcast() method:

  • The android:priority attribute that's specified in the intent filter determines the order in which the broadcast is sent.
  • If more than one receiver with same priority is present, the sending order is random.
  • The Intent is propagated from one receiver to the next.
  • During its turn, a receiver can update the Intent, or it can cancel the broadcast. (If the receiver cancels the broadcast, the Intent can't be propagated further.)

For example, the following method sends an ordered broadcast to all interested broadcast receivers:

public void sendOrderedBroadcast() {
   Intent intent = new Intent();

   // Set a unique action string prefixed by your app package name.
   intent.setAction("com.example.myproject.ACTION_NOTIFY");
   // Deliver the Intent.
   sendOrderedBroadcast(intent);
}

Local broadcasts

If you don't need to send broadcasts to a different app, use the LocalBroadcastManager.sendBroadcast() method, which sends broadcasts to receivers within your app. This method is efficient, because it doesn't involve interprocess communication. Also, using local broadcasts protects your app against some security issues.

To send a local broadcast:

  1. To get an instance of LocalBroadcastManager, call getInstance() and pass in the application context.
  2. Call sendBroadcast() on the instance. Pass in the intent that you want to broadcast.
     LocalBroadcastManager.getInstance(this).sendBroadcast(customBroadcastIntent);
    

Broadcast receivers

Broadcast receivers are app components that can register for system events or app events. When an event occurs, registered broadcast receivers are notified via an Intent. For instance, if you are implementing a media app and you're interested in knowing when the user connects or disconnects a headset, register for the ACTION_HEADSET_PLUG intent action.

Use broadcast receivers to respond to messages that have been broadcast from apps or from the Android 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 statically or dynamically.

These steps are described below.

Subclass a BroadcastReceiver

To create a broadcast receiver, define a subclass of the BroadcastReceiver class. This subclass is where Intent objects are delivered if they match the intent filters you register for.

Within your subclass:

  • Implement the onReceive() method, which is called when the BroadcastReceiver object receives an Intent broadcast .
  • Inside onReceive(), include any other logic that your broadcast receiver needs.

Example: Create a broadcast receiver

In this example, the myReceiver class a subclass of BroadcastReceiver. If the incoming broadcast intent has the ACTION_SHOW_TOAST action, the myReceiver class shows a toast message:

//Subclass of the BroadcastReceiver class.
private class myReceiver extends BroadcastReceiver {
   // Override the onReceive method to receive the broadcasts
   @Override
   public void onReceive(Context context, Intent intent) {
      //Check the Intent action and perform the required operation
        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();
       }
   }
}

The onReceive() method is called when your app receives a registered Intent broadcast. The onReceive() method runs on the main thread unless it is explicitly asked to run on a different thread in the registerReceiver() method.

The onReceive() method has a timeout of 10 seconds. After 10 seconds, the Android system considers your receiver to be blocked, and the system might show the user an "application not responding" error. For this reason, you shouldn't implement long-running operations in onReceive().

Important: Don't use asynchronous operations to run a long-running operation in your onReceive() implementation, because once your code returns from onReceive(), the system considers the BroadcastReceiver component to be finished. If onReceive() started an asynchronous operation, the system would stop the BroadcastReceiver process before the asynchronous operation had a chance to complete.

In particular:

  • Don't try to show a dialog from within a BroadcastReceiver. Instead, display a notification using the NotificationManager API.
  • Don't try to bind to a service from within a BroadcastReceiver. Instead, use Context.startService() to send a command to the service.

If you need to perform a long-running operation inside BroadcastReceiver, use WorkManager to schedule a job. When you schedule a task with WorkManager, the task is guaranteed to run. WorkManager chooses the appropriate way to run your task, based on such factors as the device API level and the app state.

Register your broadcast receiver and set intent filters

There are two types of broadcast receivers:

  • Static receivers, which you register in the Android manifest file.
  • Dynamic receivers, which you register using a context.

Static receivers

Static receivers are also called manifest-declared receivers. To register a static receiver, include the following attributes inside the <receiver> element in your AndroidManifest.xml file:

  • android:name

    The value for this attribute is the fully classified name of the BroadcastReceiver subclass, including the package name. To use the package name that's specified in the manifest, prefix the subclass name with a period, for example .AlarmReceiver.

  • android:exported (optional)

    If this boolean value is set to false, other apps cannot send broadcasts to your receiver. This attribute is important for security.

  • <intent-filter>

    Include this nested element to specify the broadcast Intent actions that your broadcast receiver component is listening for.

The following code snippet shows static registration of a broadcast receiver that listens for a custom broadcast Intent with the action "ACTION_SHOW_TOAST":

  • The receiver's name is the name of the BroadcastReceiver subclass (.AlarmReceiver).
  • The receiver is not exported, meaning that no other apps can deliver broadcasts to this app.
  • The intent filter checks whether incoming intents include an action named ACTION_SHOW_TOAST, which is a custom Intent action defined within the app.
    <receiver
     android:name=".AlarmReceiver"
     android:exported="false">
     <intent-filter>
         <action android:name=    
              "com.example.myproject.intent.action.ACTION_SHOW_TOAST"/>
     </intent-filter>
    </receiver>
    
    Note: For Android 8.0 (API level 26) and higher, static receivers can't receive most implicit broadcasts. (Implicit broadcasts are broadcasts that don't target your app specifically.) Even if you register for these broadcasts in the manifest, the Android system won't deliver them to your app. However, you can still use a dynamic receiver to register for these broadcasts.

A few broadcasts, such as ACTION_BOOT_COMPLETED and ACTION_TIMEZONE_CHANGED, are excepted from this restriction. You can declare them in the manifest, no matter what the target API level. To learn more, see the complete list of implicit broadcast exceptions.
</div>

Intent filters

An intent filter specifies the types of intents that a component can receive. When the system receives an Intent as a broadcast, it searches the broadcast receivers based on the values specified in receivers' intent filters.

To create an intent filter statically, use the <intent-filter> element in the Android manifest. An <intent-filter> element has one required element, <action>. The Android system compares the Intent action of the incoming broadcast to the filter action's android:name strings. If any of the names in the filter match the action name in the incoming broadcast, the broadcast is sent to your app.

This <intent-filter> listens for a system broadcast that's sent when the device boots up. Only Intent objects with an action named BOOT_COMPLETED match the filter:

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

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

To learn more about using intent filters, see the Intent resolution section of the Intent guide.

Dynamic receivers

Dynamic receivers are also called context-registered receivers. You register a dynamic receiver using an application context or an Activity context. A dynamic receiver receives broadcasts as long as the registering context is valid:

  • If you use the application context to register your receiver, your app receives relevant broadcasts as long as your app is running in either the foreground or the background.
  • If you use an Activity context to register your receiver, your app receives relevant broadcasts until that Activity is destroyed.

To use the context to register your receiver dynamically:

  1. Create an IntentFilter and add the Intent actions that you want your app to listen for. You can add more than one action to the same IntentFilter object.

     IntentFilter intentFilter = new IntentFilter();
     filter.addAction(Intent.ACTION_POWER_CONNECTED);
     filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
    

    When the device power is connected or disconnected, the Android system broadcasts the Intent.ACTION_POWER_CONNECTED and Intent.ACTION_POWER_DISCONNECTED intent actions.

  2. Register the receiver by calling registerReceiver() method on the context. Pass in the BroadcastReceiver object and the IntentFilter object.

     mReceiver = new AlarmReceiver();
     this.registerReceiver(mReceiver, intentFilter);
    

    In this example the Activity context (this) is used to register your receiver. So the app will receive the broadcast as long as the Activity is running.

Local broadcasts

You must register local receivers dynamically, because static registration in the manifest is not possible for a local broadcasts.

To register a receiver for local broadcasts:

  1. Get an instance of LocalBroadcastManager by calling the getInstance() method.
  2. Call registerReceiver(), passing in the receiver and an IntentFilter object.
     LocalBroadcastManager.getInstance(this)
        .registerReceiver(mReceiver, 
               new IntentFilter(CustomReceiver.ACTION_CUSTOM_BROADCAST));
    

Unregister the receiver

To save system resources and avoid leaks, unregister dynamic receivers when your app no longer needs them, or before the Activity or app is destroyed. This is also true for local broadcast receivers, because they're registered dynamically.

To unregister a normal broadcast receiver:

  1. Call unregisterReceiver() and pass in your BroadcastReceiver object:

     unregisterReceiver(mReceiver);
    

    To unregister a local broadcast receiver:

  2. Get an instance of the LocalBroadcastManager.

  3. Call LocalBroadcastManager.unregisterReceiver() and pass in your BroadcastReceiver object:

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

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

  4. Sometimes the receiver is only needed when your activity is visible, for example to disable a network function when the network is not available. In these cases, register the receiver in onResume() and unregister the receiver in onPause().

  5. You can also use the onStart()/onStop() or onCreate()/onDestroy() method pairs, if they are more appropriate for your use case.

Restricting broadcasts

An unrestricted broadcast can pose a security threat, because any registered receiver can receive it. For example, if your app uses a normal broadcast to send an implicit Intent that includes sensitive information, an app that contains malware could receive that broadcast. Restricting your broadcast is strongly recommended.

Ways to restrict a broadcast:

  • If possible, use a LocalBroadcastManager, which keeps the data inside your app, avoiding any security leaks. You can only use LocalBroadcastManager if you don't need interprocess communication or communication with other apps.
  • Use the setPackage() method and pass in the package name. Your broadcast is restricted to apps that match the specified package name.
  • Enforce access permissions on the sender side, on the receiver side, or both.

To enforce a permission when sending a broadcast:

  • Supply a non-null permission argument to sendBroadcast(). Only receivers that request this permission using the <uses-permission> tag in their AndroidManifest.xml file can receive the broadcast.

To enforce a permission when receiving a broadcast:

  • If you register your receiver dynamically, supply a non-null permission to registerReceiver().
  • If you register your receiver statically, use the android:permission attribute inside the <receiver> tag in your AndroidManifest.xml.

Best practices

  • In your intent actions, prefix String constants with your app's package name. Otherwise, you may conflict with other apps' intents. The Intent namespace is global.
  • Restrict broadcast receivers, as described above.
  • Prefer dynamic receivers over static receivers.
  • Do not start an Activity from a broadcast receiver—use a notification instead. Starting an activity from a broadcast receiver causes a bad user experience if more than one receiver is listening for the same broadcast event.
  • Never perform a long running operation in the broadcast receiver's onReceive(Context, Intent) method, because the method runs on the main UI thread. Consider using a JobScheduler or a WorkManager instead.

The related practical is 7.3: Broadcast receivers.

Learn more

results matching ""

    No results matching ""