Up to now, the focus of this book has been on activities opened directly by the user from the device’s launcher. This is the most obvious case for getting your activity up and running and making it visible to the user. And, in many cases, it is the primary way the user will start using your application.
However, remember that the Android system is based on many loosely coupled components. The things that you might accomplish in a desktop GUI via dialog boxes, child windows, and the like are mostly supposed to be independent activities. While one activity will be “special,” in that it shows up in the launcher, the other activities all need to be reached...somehow.
The “somehow” is via intents.
An intent is basically a message that you pass to Android saying, “Yo! I want to do...er...something! Yeah!” How specific the “something” is depends on the situation—sometimes you know exactly what you want to do (e.g., open one of your other activities), and sometimes you do not.
In the abstract, Android is all about intents and receivers of those intents. So, now that you are wellversed in creating activities, let’s dive into intents, so we can create more complex applications while simultaneously being “good Android citizens.”
When Sir Tim Berners-Lee cooked up the Hypertext Transfer Protocol (HTTP), he set up a system of verbs plus addresses in the form of URLs. The address indicates a resource, such as a web page, graphic, or server-side program. The verb indicates what should be done: GET to retrieve it, POST to send form data to it for processing, and so on.
Intents are similar, in that they represent an action plus context. There are more actions and more components to the context with Android intents than there are with HTTP verbs and resources, but the concept is still the same. Just as a web browser knows how to process a verb+URL pair, Android knows how to find activities or other application logic that will handle a given intent.
The two most important pieces of an intent are the action and what Android refers to as the data. These are almost exactly analogous to HTTP verbs and URLs: the action is the verb, and the data is aUri
, such as content://contacts/people/1
, representing a contact in the contacts database. Actions are constants, such as ACTION_VIEW
(to bring up a viewer for the resource), ACTION_EDIT
(to edit the resource), or ACTION_PICK
(to choose an available item given a Uri
representing a collection, such as content://contacts/people
).
If you were to create an intent combining ACTION_VIEW
with a content Uri
of content://contacts/people/1
, and pass that intent to Android, Android would know to find and open an activity capable of viewing that resource.
There are other criteria you can place inside an intent (represented as an Intent
object), besides the action and data Uri
, such as the following:
LAUNCHER
category, indicating it should appear on the launcher menu. Other activities will probably be in the DEFAULT
category or the ALTERNATIVE
category.Uri
.Bundle
of other information you want to pass along to the receiver with the intent, typically information that the receiver might want to take advantage of. Which pieces of information a given receiver can use is up to the receiver and (hopefully) is welldocumented.You will find rosters of the standard actions and categories in the Android SDK documentation for the Intent
class.
As noted in the previous section, if you specify the target component in your intent, Android has no doubt where the intent is supposed to be routed to, and it will launch the named activity. This might be fine if the target intent is in your application. It definitely is not recommended for sending intents to other applications. Component names, by and large, are considered private to the application and are subject to change. Content Uri
templates and MIME types are the preferred ways of identifying services you wish third-party code to supply.
If you do not specify the target component, then Android has to figure out which activities (or other receivers) are eligible to receive the intent. Note the use of the plural activities, as a broadly written intent might well resolve to several activities. That is the...ummm...intent (pardon the pun), as you will see later in this chapter. This routing approach is referred to as implicit routing.
Basically, there are three rules, all of which must be true for a given activity to be eligible for a given intent:
The upshot is that you want to make your intents specific enough to find the right receiver(s), and no more specific than that. This will become clearer as we work through some examples later in this chapter.
All Android components that wish to be notified via intents must declare intent filters, so Android knows which intents should go to that component. To do this, you need to add intent-filter
elements to your AndroidManifest.xml
file.
All of the example projects have intent filters defined, courtesy of the Android application-building script (android create project
or the IDE equivalent). They look something like this:
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.skeleton">
<application>
<activity android:name=".Now" android:label="Now">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Note the intent-filter
element under the activity
element. Here, we declare that this activity:
LAUNCHER
category, meaning it gets an icon in the Android main menuBecause this activity is the main one for the application, Android knows this is the component it should launch when somebody chooses the application from the main menu.
You are welcome to have more than one action or more than one category in your intent filters. That indicates that the associated component (e.g., activity) handles multiple different sorts of intents.
More than likely, you will also want to have your secondary (non-MAIN
) activities specify the MIME type of data they work on. Then, if an intent is targeted for that MIME type—either directly, or indirectly by the Uri
referencing something of that type—Android will know that the component handles such data.
For example, you could have an activity declared like this:
<activity android:name=".TourViewActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.commonsware.tour" />
</intent-filter>
</activity>
This activity will be launched by an intent requesting to view aUri
representing a vnd.android.cursor.item/vnd.commonsware.tour
piece of content. That Intent
could come from another activity in the same application (e.g., the MAIN
activity for this application) or from another activity in another Android application that happens to know aUri
that this activity handles.
In the preceding examples, the intent filters were set up on activities. Sometimes, tying intents to activities is not exactly what you want, as in the following cases:
For these cases, Android offers the receiver, defined as a class implementing the BroadcastReceiver
interface. Broadcast receivers are disposable objects designed to receive intents—specifically, broadcast intents—and take action.
The BroadcastReceiver
interface has only one method: onReceive()
. Receivers implement that method, where they do whatever it is they wish to do upon an incoming intent. To declare a receiver, add a receiver
element to your AndroidManifest.xml
file:
<receiver android:name=".MyIntentReceiverClassName" />
A receiver is alive for only as long as it takes to process onReceive()
—as soon as that method returns, the receiver instance is subject to garbage collection and will not be reused. This means receivers are somewhat limited in what they can do, mostly to avoid anything that involves any sort of callback. For example, they cannot bind to a service, and they cannot open a dialog box.
The exception is if the BroadcastReceiver
is implemented on some longer-lived component, such as an activity or service. In that case, the receiver lives as long as its “host” does (e.g., until the activity is frozen). However, in this case, you cannot declare the receiver via AndroidManifest.xml
. Instead, you need to call registerReceiver()
on your Activity
’s onResume()
callback to declare interest in an intent, and then call unregisterReceiver()
from your Activity
’s onPause()
when you no longer need those intents.
The number of actions encompassed by the Intent
class steadily grows with each new version of Android. With the release of Ice Cream Sandwich (ICS), version 4.0, Google has added a further six actions and deprecated three that are no longer required. The Android SDK documentation covers all 97 intent actions available in ICS, which you can read at your leisure. Here are some highlights to get you thinking about the possibilities:
ACTION_AIRPLANE_MODE_CHANGED
: The device has entered or exited airplane mode.ACTION_CAMERA_BUTTON
: The camera button was pressed.ACTION_DATE_CHANGED
: The date has changed. This could be important for applications like reminder lists, calendars, and so forth.ACTION_HEADSET_PLUG
: Headphones were attached or removed. This is quite important for music-playing apps and similar apps.You can start to see the possibilities as well as the complexities.
There is one hiccup with using Intent
objects to pass arbitrary messages around: it works only when the receiver is active. To quote from the documentation for BroadcastReceiver
:
If registering a receiver in your
Activity.onResume()
implementation, you should unregister it inActivity.onPause()
. (You won’t receive intents when paused, and this will cut down on unnecessary system overhead). Do not unregister inActivity.onSaveInstanceState()
, because this won’t be called if the user moves back in the history stack.
Hence, you can use the Intent
framework as an arbitrary message bus only in the following situations: