Broadcast Intents and Broadcast Receivers in Android Studio
Previous | Table of Contents | Next |
Android Implicit Intents – An Android Studio Example | Android Threads and Thread Handlers - An Android Studio Tutorial |
In addition to providing a mechanism for launching application activities, intents are also used as a way to broadcast system wide messages to other components on the system. This involves the implementation of Broadcast Intents and Broadcast Receivers, both of which are the topic of this chapter.
An Overview of Broadcast Intents
Broadcast intents are Intent objects that are broadcast via a call to the sendBroadcast(), sendStickyBroadcast() or sendOrderedBroadcast() methods of the Activity class (the latter being used when results are required from the broadcast). In addition to providing a messaging and event system between application components, broadcast intents are also used by the Android system to notify interested applications about key system events (such as the external power supply or headphones being connected or disconnected).
When a broadcast intent is created, it must include an action string in addition to optional data and a category string. As with standard intents, data is added to a broadcast intent using key-value pairs in conjunction with the putExtra() method of the intent object. The optional category string may be assigned to a broadcast intent via a call to the addCategory() method.
The action string, which identifies the broadcast event, must be unique and typically uses the application’s Java package name syntax. For example, the following code fragment creates and sends a broadcast intent including a unique action string and data:
Intent intent = new Intent(); intent.setAction("com.example.Broadcast"); intent.putExtra("MyData", 1000); sendBroadcast(intent);
The above code would successfully launch the corresponding broadcast receiver on a device running an Android version earlier than 3.0. On more recent versions of Android, however, the intent would not be received by the broadcast receiver. This is because Android 3.0 introduced a launch control security measure that prevents components of stopped applications from being launched via an intent. An application is considered to be in a stopped state if the application has either just been installed and not previously launched, or been manually stopped by the user using the application manager on the device. To get around this, however, a flag can be added to the intent before it is sent to indicate that the intent is to be allowed to start a component of a stopped application. This flag is FLAG_INCLUDE_STOPPED_PACKAGES and would be used as outlined in the following adaptation of the previous code fragment:
Intent intent = new Intent(); intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); intent.setAction("com.example.Broadcast"); intent.putExtra("MyData", 1000); sendBroadcast(intent);
An Overview of Broadcast Receivers
An application listens for specific broadcast intents by registering a broadcast receiver. Broadcast receivers are implemented by extending the Android BroadcastReceiver class and overriding the onReceive() method. The broadcast receiver may then be registered, either within code (for example within an activity), or within a manifest file. Part of the registration implementation involves the creation of intent filters to indicate the specific broadcast intents the receiver is required to listen for. This is achieved by referencing the action string of the broadcast intent. When a matching broadcast is detected, the onReceive() method of the broadcast receiver is called, at which point the method has 5 seconds within which to perform any necessary tasks before returning. It is important to note that a broadcast receiver does not need to be running all the time. In the event that a matching intent is detected, the Android runtime system will automatically start up the broadcast receiver before calling the onReceive() method.
The following code outlines a template Broadcast Receiver subclass:
package com.example.broadcastdetector; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class MyReceiver extends BroadcastReceiver { public MyReceiver() { } @Override public void onReceive(Context context, Intent intent) { // Implement code here to be performed when // broadcast is detected } }
When registering a broadcast receiver within a manifest file, a <receiver> entry must be added containing one or more intent filters, each containing the action string of the broadcast intent for which the receiver is required to listen.
The following example manifest file registers the above example broadcast receiver to listen for broadcast intents containing an action string of com.example.Broadcast:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package=" com.example.broadcastdetector.broadcastdetector " android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="17" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <receiver android:name="MyReceiver" > <intent-filter> <action android:name="com.example.Broadcast" > </action> </intent-filter> </receiver> </application> </manifest>
The same effect can be achieved by registering the broadcast receiver in code using the registerReceiver() method of the Activity class together with an appropriately configured IntentFilter object:
IntentFilter filter = new IntentFilter("com.example.Broadcast"); MyReceiver receiver = new MyReceiver(); registerReceiver(receiver, filter);
When a broadcast receiver registered in code is no longer required, it may be unregistered via a call to the unregisterReceiver() method of the activity class, passing through a reference to the receiver object as an argument. For example, the following code will unregister the above broadcast receiver:
unregisterReceiver(receiver);
It is important to keep in mind that some system broadcast intents can only be detected by a broadcast receiver if it is registered in code rather than in the manifest file. Check the Android Intent class documentation for a detailed overview of the system broadcast intents and corresponding requirements online at:
http://developer.android.com/reference/android/content/Intent.html
Obtaining Results from a Broadcast
The sendOrderedBroadcast() method is called with a number of arguments including a reference to another broadcast receiver (known as the result receiver) which is to be notified when all other broadcast receivers have handled the intent, together with a set of data references into which those receivers can place result data. When all broadcast receivers have been given the opportunity to handle the broadcast, the onReceive() method of the result receiver is called and passed the result data.
Sticky Broadcast Intents
By default, broadcast intents disappear once they have been sent and handled by any interested broadcast receivers. A broadcast intent can, however, be defined as being “sticky”. A sticky intent, and the data contained therein, remains present in the system after it has completed. The data stored within a sticky broadcast intent can be obtained via the return value of a call to the registerReceiver() method, using the usual arguments (references to the broadcast receiver and intent filter object). Many of the Android system broadcasts are sticky, a prime example being those broadcasts relating to battery level status.
A sticky broadcast may be removed at any time via a call to the removeStickyBroadcast() method, passing through as an argument a reference to the broadcast intent to be removed.
The Broadcast Intent Example
The remainder of this chapter will work through the creation of an Android Studio based example of broadcast intents in action. In the first instance, a simple application will be created for the purpose of issuing a custom broadcast intent. A corresponding broadcast receiver will then be created that will display a message on the display of the Android device when the broadcast is detected. Finally, the broadcast receiver will be modified to detect notification by the system that external power has been disconnected from the device.
Creating the Example Application
Launch Android Studio and create a new project, entering SendBroadcast into the Application name field and ebookfrenzy.com as the Company Domain setting before clicking on the Next button.
On the form factors screen, enable the Phone and Tablet option and set the minimum SDK setting to API 8: Android 2.2 (Froyo). Continue to proceed through the screens, requesting the creation of a blank activity named SendBroadcastActivity with corresponding layout and menu resource files named activity_send_broadcast and menu_send_broadcast respectively.
Once the new project has been created, locate and load the activity_send_broadcast.xml layout file located in the Project tool window under app -> res -> layout and, with the Designer tool in Design mode, replace the TextView object with a Button view. Move the button to the center of the display, double click on it and set the text property so that it reads “Send Broadcast”. Once the text value has been set, use the light bulb menu icon to extract the string to a resource named button_text.
With the button still selected in the layout, locate the onClick property in the Properties panel and configure it to call a method named broadcastIntent. A useful shortcut for finding properties is to click on the toolbar of the Properties panel and start typing the first few characters of the property name. This will trigger the search system and begin selecting the best matches for the property name.
Creating and Sending the Broadcast Intent
Having created the framework for the SendBroadcast application, it is now time to implement the code to send the broadcast intent. This involves implementing the broadcastIntent() method specified previously as the onClick target of the Button view in the user interface. Locate and double click on the SendBroadcastActivity.java file and modify it to add the code to create and send the broadcast intent. Once modified, the source code for this class should read as follows:
package com.ebookfrenzy.sendbroadcast; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.content.Intent; import android.view.View; public class SendBroadcastActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_send_broadcast); } public void broadcastIntent(View view) { Intent intent = new Intent(); intent.setAction("com.ebookfrenzy.sendbroadcast"); intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); sendBroadcast(intent); } . . . }
Note that, in this instance, the action string for the intent is com.ebookfrenzy.sendbroadcast. When the broadcast receiver class is created in later sections of this chapter, it is essential that the intent filter declaration match this action string.
This concludes the creation of the application to send the broadcast intent. All that remains is to build a matching broadcast receiver.
Creating the Broadcast Receiver
In order to create the broadcast receiver, a new class needs to be created which subclasses the BroadcastReceiver superclass. Create a new project with the application name set to BroadcastReceiver and the company domain name set to com.ebookfrenzy, this time selecting the Add No Activity option before clicking on Finish.
Within the Project tool window, navigate to app -> java and right click on the package name. From the resulting menu, select the New -> Other -> Broadcast Receiver menu option, name the class MyReceiver and make sure the Exported and Enabled options are selected.
Once created, Android Studio will automatically load the new MyReceiver.java class file into the editor where it should read as follows:
package com.ebookfrenzy.broadcastreceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class MyReceiver extends BroadcastReceiver { public MyReceiver() { } @Override public void onReceive(Context context, Intent intent) { // TODO: This method is called when the BroadcastReceiver is receiving // an Intent broadcast. throw new UnsupportedOperationException("Not yet implemented"); } }
As can be seen in the code, Android Studio has generated a template for the new class and generated a stub for the onReceive() method. A number of changes now need to be made to the class to implement the required behavior. Remaining in the MyReceiver.java file, therefore, modify the code so that it reads as follows:
package com.ebookfrenzy.broadcastreceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class MyReceiver extends BroadcastReceiver { public MyReceiver() { } @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "Broadcast Intent Detected.", Toast.LENGTH_LONG).show(); } }
The code for the broadcast receiver is now complete.
Configuring a Broadcast Receiver in the Manifest File
In common with other Android projects, BroadcastReceiver has associated with it a manifest file named AndroidManifest.xml.
This file needs to publicize the presence of the broadcast receiver and must include an intent filter to specify the broadcast intents in which the receiver is interested. When the BroadcastReceiver class was created in the previous section, Android Studio automatically added a <receiver> element to the manifest file. All that remains, therefore, is to add an <intent-filter> element within the <receiver> declaration appropriately configured for the custom action string:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ebookfrenzy.broadcastreceiver.broadcastreceiver" > <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <receiver android:name=".MyReceiver" android:enabled="true" android:exported="true" > <intent-filter> <action android:name="com.ebookfrenzy.sendbroadcast" > </action> </intent-filter> </receiver> </application> </manifest>
With the manifest file completed, the broadcast example is ready to be tested.
Testing the Broadcast Example
In order to test the broadcast sender and receiver, begin by running the BroadcastReceiver application on a physical Android device or AVD. When the Edit configuration dialog appears, select the Do not launch Activity option so that the activity is installed but not launched and click on the Run button. The successful installation of the package will be reported in the Android Studio Run tool window:
Uploading file local path: C:\Users\Neil\AndroidStudioProjects\BroadcastReceiver\app\build\outputs\apk\app-debug.apk remote path: /data/local/tmp/com.ebookfrenzy.broadcastreceiver Installing com.ebookfrenzy.broadcastreceiver DEVICE SHELL COMMAND: pm install -r "/data/local/tmp/com.ebookfrenzy.broadcastreceiver" pkg: /data/local/tmp/com.ebookfrenzy.broadcastreceiver Success
Once the receiver is installed, run the SendBroadcast application on the same device or AVD and wait for it to appear on the display. Once running, touch the button, at which point the toast message reading “Broadcast Intent Detected.” should pop up for a few seconds before fading away.
In the event that the toast message does not appear, double check that the BroadcastReceiver application installed correctly and that the intent filter in the manifest file matches the action string used when the intent was broadcast.
Listening for System Broadcasts
The final stage of this example is to modify the intent filter for the BroadcastReceiver to listen also for the system intent that is broadcast when external power is disconnected from the device. The action that the receiver needs to be listening for in this context is android.intent.action.ACTION_POWER_DISCONNECTED. The modified manifest file for the BroadcastReceiver project should, therefore, now read as follows:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ebookfrenzy.broadcastreceiver.broadcastreceiver"> <application android:allowBackup="true" android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:theme="@style/AppTheme"> <receiver android:name=".MyReceiver" android:enabled="true" android:exported="true" > <intent-filter> <action android:name="com.ebookfrenzy.sendbroadcast" > </action> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" > </action> </intent-filter> </receiver> </application> </manifest>
Since the onReceive() method is now going to be listening for two types of broadcast intent, it is worthwhile modifying the code so that the action string of the current intent is also displayed in the toast message. This string can be obtained via a call to the getAction() method of the intent object passed as an argument to the onReceive() method:
public void onReceive(Context context, Intent intent) { String message = "Broadcast intent detected " + intent.getAction(); Toast.makeText(context, message, Toast.LENGTH_LONG).show(); }
Test the receiver by re-installing the modified BroadcastReceiver package. Touching the button in the SendBroadcast application should now result in a new message containing the custom action string:
Broadcast intent detected com.ebookfrenzy.sendbroadcast
Next, remove the USB connector that is currently supplying power to the Android device, at which point the receiver should report the following in the toast message: Broadcast intent detected android.intent.action.ACTION_POWER_DISCONNECTED
Summary
Broadcast intents are a mechanism by which an intent can be issued for consumption by multiple components on an Android system. Broadcasts are detected by registering a Broadcast Receiver, which, in turn, is configured to listen for intents that match particular action strings. In general, broadcast receivers remain dormant until woken up by the system when a matching intent is detected. Broadcast intents are also used by the Android system to issue notifications of events such as a low battery warning or the connection or disconnection of external power to the device.
In addition to providing an overview of Broadcast intents and receivers, this chapter has also worked through an example of sending broadcast intents and the implementation of a broadcast receiver to listen for both custom and system broadcast intents.
Previous | Table of Contents | Next |
Android Implicit Intents – An Android Studio Example | Android Threads and Thread Handlers - An Android Studio Tutorial |