3 Comments

Creating BroadcastReceivers that Run while Your Android App Is Backgrounded

In my latest project, I’m building iOS and Android apps that have to handle incoming Bluetooth data even when the apps are backgrounded. To achieve this on Android, I decided to implement a BroadcastReceiver.

The example shown here is listening for a Bluetooth “bond state change,” which indicates whether a Bluetooth device has become bonded (paired) with the phone, un-bonded (unpaired), or is attempting to pair. My project is using Xamarin, so the code shown here is in C#. However, the concepts are similar in native Android development, although with some syntax differences.

Services or BroadcastReceivers?

Implementing a BroadcastReceiver component wasn’t my only option. I could have used a Service component as well. What’s the difference? Services are meant for actions that need to be performed continuously, even while the app is backgrounded. Examples include playing music or providing GPS directions while the user is viewing other apps.

A BroadcastReceiver is meant for a one-shot action in response to an intent.  An intent is an asynchronous message that is broadcast when a certain action  happens.  Android apps can send or receive broadcast messages from the Android system and other Android apps in a “pub sub” type pattern.  

An example of an intent might be when “airplane mode” is turned on or off.  A BroadcastReceiver would listen for that intent, and upon receiving a broadcast message about it, would execute some task (after that, it would wait for the next intent broadcast).  As such, it functions sort of like an interrupt, alerting your application to an important change in the overall system.

Note: As of API level 26, there are changes to BroadcastReceiver as described here.  

Creating a BroadcastReceiver

Any BroadcastReceivers you create must subclass from BroadcastReceiver. I made a new file (called BluetoothReceiver.cs) to implement a receiver that will trigger on a Bluetooth bond state change.

I’ve used the BroadcastReceiver attribute on my BluetoothReceiver class, and I have given it a name.  Then I set Exported to true, so that the receiver is accessible by the rest of the phone, rather than just internal to my phone app.  When I register the BroadcastReceiver in the next step, I will specify which intent action will trigger this receiver.

Next, I have to implement the OnReceive function. For this example, I’m printing out the action name and Bluetooth device by accessing an “Extra.”  Sometimes, more information accompanies actions and intents in the form of an Extra.  You can find out which Extras are available by looking at the Android documentation for that action.

[BroadcastReceiver(Name = "com.yourWorkspace.yourProject.BluetoothReceiver", Enabled = true, Exported = true)]
public class BluetoothReceiver : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        string action = intent.Action;
        Console.WriteLine("action is: " + action);
        Android.Bluetooth.BluetoothDevice device = (Android.Bluetooth.BluetoothDevice)intent.GetParcelableExtra("android.bluetooth.device.extra.DEVICE");
        Console.WriteLine("Value of extra DEVICE " + device);
    }
}

Registering a BroadcastReceiver

Next, I must register the BroadcastReceiver. I have two options here: register it from an Activity, or register it in the AndroidManifest.xml.

If I register the BroadcastReceiver from an Activity, it will run when the Activity is running. However, it will be subject to the Android Activity Lifecycle.

registerReceiver(BluetoothReceivers, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));

The alternative is to register the receiver in the AndroidManifest. This ensures that the BroadcastReceiver runs all the time, even when the app is backgrounded. I chose this option because I want my application to receive Bluetooth events even when the app isn’t in use (i.e. backgrounded).

In my AndroidManifest.xml file, I add the BroadcastReceiver within the <application> tags.

<receiver android:name="com.yourWorkspace.yourProject.BluetoothReceiver" android:exported="true">
    <intent-filter>
        <action android:name="android.bluetooth.device.action.BOND_STATE_CHANGED" />
    </intent-filter>
</reciever>

You can include multiple actions per receiver, and then filter those actions within the OnReceive() function of the receiver.  For example, when I originally registered the BroadcastReceiver in the Android Manifest, I included an “airplane mode” action. While I didn’t need that functionality, it was easy to test, so I could verify that my receiver was working (without wondering if the problem was on the Bluetooth device or in my code).

<action android:name="android.intent.action.AIRPLANE_MODE" />

Now, when you run your app on an Android phone (connected to your laptop for debugging), you should see console prints in the Application Output window when you trigger the intent action, even if the app is backgrounded.