1. Model
- An Intent Sender is an object which encapsulates
- an Application Identity
- an Intent
- a function to perform with the Intent
- Intent Senders are capable of performing four functions
- starting an Activity
- returning a result to an Activity
- broadcasting an Intent
- starting a Service
- The function a given Intent Sender is capable of performing is fixed at the point it is constructed.
- An Intent Sender is used to perform a function with an Intent as though it was being done by the Application whose identity it encapsulates.
- An Intent Sender can be constructed such that it can only be used once
- An Intent Sender can be cancelled after which it can no longer be used
2. The IntentSender Class
An Intent Sender is represented by an instance of the
android.content.IntentSender
class.
2.1 Obtaining An IntentSender
An IntentSender instance cannot be constructed directly but one can be obtained from a PendingIntent instance.
2.2 Transfering An IntentSender Instance Between Applications
The IntentSender class implements the android.os.Parcelable interface so an IntentSender instance can be written to a Parcel using the canonical
public void writeToParcel(Parcel out, int flags)
method.
Alternatively an IntentSender instance can be written to a Parcel using the method
public static void writeIntentSenderOrNullToParcel(IntentSender sender, Parcel out)
and read from one using the corresponding method
public static IntentSender readIntentSenderOrNullFromParcel(Parcel in)
Note
The documentation for the readIntentSenderOrNullFromParcel() method reads
Convenience function for reading either a Messenger or null pointer from a Parcel. You must have previously written the Messenger with
writeIntentSenderOrNullToParcel(IntentSender, Parcel).
which is a bit confusing.
3. The PendingIntent Class
An instance of android.app.PendingIntent encapsulates an IntentSender.
A PendingIntent instance is a reference to an object which represents the PendingIntent and which exists independently of the Application which created it and those that use it. Multiple distinct PendingIntent instances in the same process can all refer to the same PendingIntent object.
The function to be performed by the IntentSender encapsulated by a given PendingIntent is determined by the way in which it is constructed.
3.1 Obtaining A PendingIntent: The Factory Methods
There are three static PendingIntentfactory methods which can be used to obtain a PendingIntent
public static PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags)
public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags)
public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags)
These return PendingIntent instances which can be used to
respectively.
Depending upon the given arguments each of these methods can either
- create a new PendingIntent object,
- modify an existing PendingIntent object,
- modify an existing PendingIntent object and create a new PendingIntent object, or
- do nothing
3.1.1 Factory Method Arguments
3.1.1.1 The Context
The context argument is used to identify the Application creating and/or modifying the PendingIntent object(s).
3.1.1.2 The RequestCode
According to the documentation the requestCode is not currently used
. This is not strictly true. See here
3.1.1.3 The Intent
The intent argument is used to specify the Intent to encapsulate when creating a new PendingIntent object, or the values to use when modifying an existing PendingIntent object.
3.1.1.4 The Flags
The flags argument is dual-purpose.
It can be used to pass the following flags defined as PendingIntent class constants
FLAG_CANCEL_CURRENTFLAG_NO_CREATEFLAG_ONE_SHOTFLAG_UPDATE_CURRENT
as required.
It can also also used to pass any of the flags defined by the Intent class constants which can be passed to the Intent.fillIn() method.
3.1.1.4.1 The PendingIntent Flags
3.1.1.4.1.1 FLAG_CANCEL_CURRENT
If this flag is specified and a matching PendingIntent exists then it is cancelled.
3.1.1.4.1.2 FLAG_NO_CREATE
If this flag is specified a new PendingIntent instance will not be created.
3.1.1.4.1.3 FLAG_ONE_SHOT
If this flag is specified and a new PendingIntent is created then it can only be used once.
3.1.1.4.1.4 FLAG_UPDATE_CURRENT
If this flag is specified and a matching PendingIntent exists then it will be updated with the …
3.1.1.4.2 The Intent Flags
The Intent sent by an IntentSender is a copy of the encapsulated Intent. This copy may be modified using the Intent.fillIn() method if an additional Intent is supplied by the user of the IntentSender. The Intent flags specified at this point are passed to the Intent.fillIn() method with the Intent supplied by the caller.
3.1.2 Factory Method PendingIntent Object Matching
When an Application calls one of the PendingIntent factory methods the arguments to that method will match an existing PendingIntent object, if and only if the following are all true
- the
requestCodeargument is equal to the requestCode specified when the PendingIntent was created - the Intent specified by the
intentargument is equal to the Intent specified when the PendingIntent was created,as defined byIntent.filterEquals()method - the
flagsargument excluding the values of theFLAG_CANCEL_CURRENTFLAG_NO_CREATE, andFLAG_UPDATE_CURRENT
flags is equal to the flags specified when the PendingIntent was created, excluding the same flag values as above
- the function to perform is the same as that specified when the PendingIntent was created
3.1.2.1 Examples
If we assume that there are currently no existing PendingIntent objects and that an Activity in an Application executes the following
Intent i1 = new Intent("xper.intentsender.intent.AN_INTENT");
Intent i1a = new Intent("xper.intentsender.intent.AN_INTENT").putExtra("AN_EXTRA", "An extra value");
Intent i2 = new Intent("xper.intentsender.intent.AN_INTENT").setType("type/subtype");
// identical
PendingIntent pi1 = PendingIntent.getActivity(this, 1, i1, 0);
PendingIntent pi2 = PendingIntent.getActivity(this, 1, i1, 0);
// different request codes
PendingIntent pi3 = PendingIntent.getActivity(this, 2, i1, 0);
// matching Intents
PendingIntent pi4 = PendingIntent.getActivity(this, 1, i1a, 0);
// different Intents
PendingIntent pi5 = PendingIntent.getActivity(this, 1, i2, 0);
// different flags one
PendingIntent pi6 = PendingIntent.getActivity(this, 1, i1, PendingIntent.FLAG_ONE_SHOT);
// different flags two
PendingIntent pi7= PendingIntent.getActivity(this, 1, i1, Intent.FILL_IN_ACTION);
// different functions
PendingIntent pi8 = PendingIntent.getBroadcast(this, 1, i1, 0);
then
pi1refers to a new PendingIntent objectpi2refers to the same PendingIntent object aspi1pi3refers to a new PendingIntent objectpi4refers to the same PendingIntent object aspi1andpi2pi5refers to a new PendingIntent objectpi6refers to a new PendingIntent objectpi7refers to a new PendingIntent objectpi8refers to a new PendingIntent object
3.1.3 Factory Method Return Values
3.1.3.1 Case One
If there is no existing PendingIntent object which matches and FLAG_NO_CREATE is specified a factory method will return null.
For example, if we assume there are currently no existing PendingIntent objects, that
Intent i1 = new Intent("xper.intentsender.intent.AN_INTENT_ONE");
Intent i2 = new Intent("xper.intentsender.intent.AN_INTENT_TWO");
Intent i3 = new Intent("xper.intentsender.intent.AN_INTENT_THREE");
and that the following code is executed by an Activity within an Application, then
PendingIntent.getBroadcast(this, 1, i1, PendingIntent.FLAG_NO_CREATE);
will return
null
3.1.3.2 Case Two
If there is no existing PendingIntent object which matches and the FLAG_NO_CREATE is not specified then a factory method will return a PendingIntent instance which refers to a new PendingIntent object.
Continuing with the example, if the Activity now executes the following
PendingIntent pi1 = PendingIntent.getBroadcast(this, 1, i1, 0);
PendingIntent pi2 = PendingIntent.getBroadcast(this, 1, i2, 0);
PendingIntent pi3 = PendingIntent.getBroadcast(this, 1, i3, 0);
then pi1, pi2, and pi3 all necessarily refer to new PendingIntent objects.
3.1.3.3 Case Three
If there is an existing PendingIntent object which matches and the FLAG_NO_CREATE is specified then a factory method will return a PendingIntent instance which refers to the existing PendingIntent object.
Continuing with the example again, if the Activity now executes the following
PendingIntent pi4 = PendingIntent.getBroadcast(this, 1, i1, PendingIntent.FLAG_NO_CREATE);
PendingIntent pi5 = PendingIntent.getBroadcast(this, 1, i2, PendingIntent.FLAG_CANCEL_CURRENT|PendingIntent.FLAG_NO_CREATE);
PendingIntent pi6 = PendingIntent.getBroadcast(this, 1, i3, PendingIntent.FLAG_UPDATE_CURRENT|PendingIntent.FLAG_NO_CREATE);
then
pi4refers to the same PendingIntent object aspi1pi5refers to the same PendingIntent object aspi2and it has been cancelledpi6refers to the same PendingIntent object aspi3
3.1.3.4 Case Four
If there is an existing PendingIntent object which matches and the FLAG_NO_CREATE is not specified then a factory method will either return a PendingIntent instance which refers to a new PendingIntent object, or a PendingIntent instance which refers to the existing PendingIntent object.
Continuing with the example again, if the Activity now executes the following
PendingIntent pi7 = PendingIntent.getBroadcast(this, 1, i1, PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pi8 = PendingIntent.getBroadcast(this, 1, i3, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pi9 = PendingIntent.getBroadcast(this, 1, i1, 0);
then
- the PendingIntent referenced by
pi1andpi4has been cancelled pi7refers to a new PendingIntent objectpi8refers to the same PendingIntent object aspi3andpi6pi9refers to the same PendingIntent object aspi7
3.2 Cancelling A PendingIntent
A PendingIntent object can be cancelled by calling the PendingIntent instance
public void cancel()
method.
A SecurityException will be thrown if the calling Application did not create the PendingIntent object which is referenced by the PendingIntent instance.
3.3 Transfering A PendingIntent Instance Between Applications
The PendingIntent class implements the android.os.Parcelable interface so a PendingInstance instance can be written to a Parcel using the canonical
public void writeToParcel (Parcel out, int flags)
method.
Alternatively a PendingIntent instance can be writen to a Parcel using the method
public static void writePendingIntentOrNullToParcel(PendingIntent sender, Parcel out)
and read from one using the corresponding method
public static PendingIntent readPendingIntentOrNullFromParcel(Parcel in)
Note
The documentation for the readPendingIntentOrNullFromParcel() method suffers from the same problem as the documentation for the corresponding method in the IntentSender class
4. Obtaining A PendingIntent Using Activity Methods
There is a single android.app.Activity method which can be used to obtain a PendingIntent object.
The method
public PendingIntent createPendingResult(int requestCode, Intent data, int flags)
will return a PendingIntent instance which can be used to return results to the Activity which invoked this method via its Activity.onActivityResult() method
The requestCode, data and flags arguments are identical to the requestCode, intent and flags arguments of the PendingIntent factory methods.
5. Using An IntentSender
5.1 The sendIntent() Method
The function encapsulated by an IntentSender can be performed using its
public void sendIntent(
Context context,
int code,
Intent intent,
IntentSender.OnFinished onFinished,
Handler handler)
method.
5.1.1 Arguments
5.1.1.1 The Context Argument
The context argument is only required if the intent argument is not null when it is used to obtain a ContentResolver.
5.1.1.2 The Code Argument
The semantics of the code argument are determined by the function being performed by the IntentSender.
5.1.1.3 The Intent Argument
The intent argument can be used to specify an Intent to use to create a modified version of the encapsulated Intent. The modification is performed using the Intent.fillIn() method. If this argument is not null then the parts of the Intent which will be used are specified by the set of Intent flags supplied when the IntentSender was created.
5.1.1.4 The OnFinished Argument
The onFinished argument can be used to specify a callback object which implements the IntentSender.OnFinished interface. This interface defines a single method
public void onSendFinished(
IntentSender intentSender,
Intent intent,
int resultCode,
String resultData,
Bundle resultExtras)
which will be invoked when the function being performed by the IntentSender has been completed. The exact semantics are determined by the function being performed.
5.1.1.5 The Handler Argument
The handler argument can be used to specify the Handler in which the callback object, if any, specified by the onFinished argument should be run.
5.2 Using An IntentSender To Start An Activity
When an IntentSender is being used to start an Activity the code argument is not used and if the onFinished argument is not null
then the onSendFinished() method is invoked with values of
0nullnull
for the
resultCoderesultDataresultExtras
arguments respectively.
If the IntentSender is no longer valid, for example, it has been cancelled, then an instance of the class IntentSender.SendException will be thrown.
5.3 Using An IntentSender To Return A Result To An Activity
When an IntentSender is being used to return a result to an Activity then code argument is passed as the resultCode argument of the Activity.onActivityResult() method.
If the onFinished argument is not null then the onSendFinished() method is invoked with values of
0nullnull
for the
resultCoderesultDataresultExtras
arguments respectively.
If the IntentSender is no longer valid, for example, it has been cancelled, then an instance of the class IntentSender.SendException will be thrown.
5.4 Using An IntentSender To Broadcast An Intent
Although it is not clear from the documentation when an IntentSender is used to broadcast an Intent the onFinished argument controls how the broadcast is performed.
5.4.1 Normal Broadcast
If the onFinished argument is null then a normal broadcast is performed.
In this case the code argument is not used.
5.4.2 Ordered Broadcast
If the onFinished argument is not null then an ordered broadcast is performed.
In this case the code argument is equivalent to the initialCode argument of the long-form of the
Context.sendOrderedBroadcast() method.
When the ordered broadcast completes the onSendFinished() method is invoked with the results, with the
resultCoderesultDataresultExtras
arguments being the values which would be returned from calls to the corresponding accessor methods in a BroadcastReceiver.onReceive() method.
For example, if we create a PendingIntent instance as follows
PendingIntent.getBroadcast(this, 3, new Intent("xper.example.ORDERED_BROADCAST_INTENT"), 0);
and we use the encapsulated IntentSender to run this example
is.sendIntent(
this,
0,
null,
new ResultReceiver(),
null)
where the ResultReceiver class is defined as
public class ResultReceiver
implements
IntentSender.OnFinished
{
public void onSendFinished(
IntentSender intentSender,
Intent intent,
int resultCode,
String resultData,
Bundle resultExtras)
{
System.out.println("ResultReceiver.onSendFinished()");
System.out.println(intentSender);
System.out.println(intent);
System.out.println("\tgetResultCode() => " + resultCode);
System.out.println("\tgetResultData() => " + resultData);
Bundle extras = resultExtras != null ? resultExtras : new Bundle();
System.out.println("\tBegin Extras");
for (String key : extras.keySet())
{
System.out.print("\t\t");
System.out.print(key);
System.out.print("\t=> ");
System.out.println(extras.get(key));
}
System.out.println("\tEnd Extras");
System.out.println("ResultReceiver.onSendFinished() done");
}
}
then when the broadcast completes the ResultReceiver.onSendFinished() method prints
ResultReceiver.onSendFinished()
IntentSender{4051f870: android.os.BinderProxy@4051f838}
Intent { act=xper.example.ORDERED_BROADCAST_INTENT }
getResultCode() => 341
getResultData() => null, Three, TwoB, TwoA, One
Begin Extras
Three.value => Three
TwoB.value => TwoB
TwoA.value => TwoA
One.value => One
End Extras
ResultReceiver.onSendFinished() done
The output is not exactly the same as in the original example because the initialData cannot be specified.
5.5 Using An IntentSender To Start A Service
When an IntentSender is being used to start a Service the code argument is not used and if the onFinished argument is not null
then the onSendFinished() method is invoked with values of
0nullnull
for the
resultCoderesultDataresultExtras
arguments respectively.
If the IntentSender is no longer valid, for example, it has been cancelled, then an instance of the class IntentSender.SendException will be thrown.
6. Using An IntentSender From An Activity
The android.app.Activity class defines two methods which can be used to perform the function encapsulated by an IntentSender.
public void startIntentSender(
IntentSender intent,
Intent fillInIntent,
int flagsMask,
int flagsValues,
int extraFlags)
public void startIntentSenderForResult(
IntentSender intent,
int requestCode,
Intent fillInIntent,
int flagsMask,
int flagsValues,
int extraFlags)
They are intended to be analagous to the methods
public void startActivity(Intent intent)
and
public void startActivityForResult(Intent intent, int requestCode)
respectively, but despite this they can in fact be used with any IntentSender not just those which start Activities.
The arguments in common are the same for both methods.
The fillInIntent argument is equivalent to the intent argument of the IntentSender.sendIntent() method.
The flagsMask and flagsValues can be used to modify the flags of the Intent sent by the IntentSender. Effectively
flags = ((originalFlags & ~flagsMask) | (flagValues & flagsMask))
In practice it is not done quite like this for security reasons. Note that this makes it possible to turn off flags which cannot be done using the Intent.fillIn() method.
The extraFlags argument is not used.
If the startIntentSenderForResult() is used to start an Activity then once it has finished, the calling Activity’s onActivityResult() method will be called as normal.
7. Using A PendingIntent
The PendingIntent class defines no less than five methods which can be used to invoke the encapsulated IntentSender
public void send()
public void send(int code)
public void send(Context context, int code, Intent intent)
public void send(int code, PendingIntent.OnFinished onFinished, Handler handler)
public void send(
Context context,
int code,
Intent intent,
PendingIntent.OnFinished onFinished,
Handler handler)
Of these the first four are simply wrapper methods around the fifth, providing the appropriate defaults as necessary.
The behaviour of the fifth method is identical to that of the IntentSender.sendIntent() method.
Copyright (c) 2011 By Simon Lewis. All Rights Reserved.