Intent class.
Intent Routing
As previously noted, if you specify the target component in your intent, Android has no doubt where the intent is supposed to be routed to — it will launch the named activity. This might be OK 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 what activities (or other intent 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:
1. The activity must support the specified action.
2. The activity must support the stated MIME type (if supplied).
3. The activity must support all of the categories named in the 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.
Stating Your Intent(ions)
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 (activityCreator
or the IDE equivalent). They look something like this:
<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:
• is the main activity for this application
• is in the LAUNCHER
category, meaning it gets an icon in the Android main menu
Because 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 get launched by an intent requesting to view a Uri
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 a Uri
that this activity handles.
Narrow Receivers
In the examples shown previously, the intent filters were set up on activities. Sometimes, tying intents to activities is not exactly what we want:
• Some system events might cause us to want to trigger something in a service rather than an activity.
• Some events might need to launch different activities in different circumstances, where the criteria are not solely based on the intent itself, but some other state (e.g., if we get intent X and the database has a Y, then launch activity M; if the database does not have a Y, then launch activity N).
For these cases, Android offers the intent receiver, defined as a class implementing the BroadcastReceiver
interface. Intent receivers are disposable objects designed to receive intents — particularly broadcast intents — and take action, typically involving launching other intents to trigger logic in an activity, service, or other component.
The BroadcastReceiver
interface has only one method: onReceive()
. Intent receivers implement that method, where they do whatever it is they wish to do upon an incoming intent. To declare an intent receiver, add a receiver element to your AndroidManifest.xml
file:
<receiver android:name='.MyIntentReceiverClassName' />
An intent receiver is only alive for 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 intent 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 intent receiver lives as long as its “host” does (e.g., until the activity is frozen). However, in this case, you cannot declare the intent receiver via AndroidManifest.xml
. Instead, you need to call registerReceiver()
on your Activity
’s onResume()
callback to declare interest in an intent, then call unregisterReceiver()
from your Activity
’s onPause()
when you no longer need those intents.