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 PendingIntent
factory 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_CURRENT
FLAG_NO_CREATE
FLAG_ONE_SHOT
FLAG_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
requestCode
argument is equal to the requestCode specified when the PendingIntent was created - the Intent specified by the
intent
argument is equal to the Intent specified when the PendingIntent was created,as defined byIntent.filterEquals()
method - the
flags
argument excluding the values of theFLAG_CANCEL_CURRENT
FLAG_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
pi1
refers to a new PendingIntent objectpi2
refers to the same PendingIntent object aspi1
pi3
refers to a new PendingIntent objectpi4
refers to the same PendingIntent object aspi1
andpi2
pi5
refers to a new PendingIntent objectpi6
refers to a new PendingIntent objectpi7
refers to a new PendingIntent objectpi8
refers 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
pi4
refers to the same PendingIntent object aspi1
pi5
refers to the same PendingIntent object aspi2
and it has been cancelledpi6
refers 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
pi1
andpi4
has been cancelled pi7
refers to a new PendingIntent objectpi8
refers to the same PendingIntent object aspi3
andpi6
pi9
refers 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
0
null
null
for the
resultCode
resultData
resultExtras
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
0
null
null
for the
resultCode
resultData
resultExtras
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
resultCode
resultData
resultExtras
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
0
null
null
for the
resultCode
resultData
resultExtras
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.
Leave a comment