Chapter
17
171
172
Pieces of Intents
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 a Uri, 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. You can place other criteria inside an intent (represented as an Intent object), besides the action and data Uri, such as the following: Category: Your main activity will be in the LAUNCHER category, indicating it should show up on the launcher menu. Other activities will probably be in the DEFAULT or ALTERNATIVE categories. MIME type: This indicates the type of resource on which you want to operate, if you dont know a collection Uri. Component: This is the class of the activity that is supposed to receive this intent. Using components this way obviates the need for the other properties of the intent. However, it does make the intent more fragile, as it assumes specific implementations. Extras: A Bundle of other information you want to pass along to the receiver with the intent, 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 well-documented. You will find rosters of the standard actions and categories in the Android SDK documentation for the Intent class.
Intent Routing
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 appropriate 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, Android must figure out which activities (or other intent receivers) are eligible to receive the intent. Note the use of the plural
173
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 activity must support the specified action. The activity must support the stated MIME type (if supplied). 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 correct receiver(s), and no more specific than that. This will become clearer as we work through some examples later in this chapter.
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 someone 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.
174
More than likely, you will also want to have your secondary (non-MAIN) activities specify the MIME type of data on which they work. Then, if an intent is targeted for that MIME typeeither 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 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 preceding examples, the intent filters were set up on activities. Sometimes, tying intents to activities is not exactly what you want, as in these cases: Some system events might cause you 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 you 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 intentsparticularly broadcast intentsand take action. The action typically involves 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 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 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.
175
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 Activitys onResume() callback to declare interest in an intent, and then call unregisterReceiver() from your Activitys onPause() when you no longer need those intents.
If registering a receiver in your Activity.onResume() implementation, you should unregister it in Activity.onPause(). (You wont receive intents when paused, and this will cut down on unnecessary system overhead). Do not unregister in Activity.onSaveInstanceState(), because this wont 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: Your receiver does not care if it misses messages because it was not active. You provide some means of getting the receiver caught up on messages it missed while it was inactive. In Chapters 29 and 30, you will see an example of the former condition, where the receiver (service client) will use Intent-based messages when they are available, but does not need them if the client is not active.
176