Just An Application

February 25, 2011

The Android Intent APIs: Part Eight – Android 3.0 Additions

Given the amount you could already do to and with a common or garden Intent it is difficult to see what else they could have added in the Honeycomb release but they have managed to come up with a few things.

1. The Intent Class

The Intent class has acquired two rather specialized factory methods.

The method

    public static Intent makeMainActivity(ComponentName mainActivity)

will return an Intent which can be used to launch an Application with the named main Activity.

For example,

    Intent i = Intent.makeMainActivity(
                          new ComponentName(
                                  "xper.honeycomb",
                                  "xper.honycomb.XperActivity"));
        
    System.out.println("action     == " + i.getAction());
    System.out.println("categories == " + i.getCategories());
    System.out.println("component  == " + i.getComponent());
    System.out.println("flags      == 0x" + Integer.toHexString(i.getFlags()));

prints

    action     == android.intent.action.MAIN
    categories == [android.intent.category.LAUNCHER]
    component  == ComponentInfo{xper.honeycomb/xper.honycomb.XperActivity}
    flags      == 0x0

The method

    public static Intent makeRestartActivityTask(ComponentName mainActivity)

will return an Intent to re-launch an Application with the named main Activity.

For example,

    Intent j = Intent.makeRestartActivityTask(
                          new ComponentName(
                                  "xper.honeycomb", 
                                  "xper.honycomb.XperActivity"));
        
    System.out.println("action     == " + j.getAction());
    System.out.println("categories == " + j.getCategories());
    System.out.println("component  == " + j.getComponent());
    System.out.println("flags      == 0x" + Integer.toHexString(j.getFlags()));

prints

    action     == android.intent.action.MAIN
    categories == [android.intent.category.LAUNCHER]
    component  == ComponentInfo{xper.honeycomb/xper.honycomb.XperActivity}
    flags      == 0x10008000

As can be seen the difference between the two methods is that the second one sets two flags,

    Intent.FLAG_ACTIVITY_NEW_TASK(0x10000000)

and

    Intent.FLAG_ACTIVITY_CLEAR_TASK(0x00008000))

Quite why it is necessary to add two brand new methods for this purpose is not that obvious.

2. Activities And Intents

The method

    public abstract void startActivities(Intent[] intents)

has been added to the class android.app.Content

An implementation of this method will effectively start a stack of Activities with the Activity at the bottom corresponding to the first Intent in the array and the Activity at the top corresponding to the last Intent in the array. When started in this way an Activity is not created until it is actually accessed by the User

For example, if an Application defines the following Activities

    <activity 
        android:name  = "Foo" 
        android:label = "Foo">
        <intent-filter>
            <action 
                android:name = "xper.intent.FOO_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>
    <activity 
        android:name  = "Bar" 
        android:label = "Bar">
        <intent-filter>
            <action 
                android:name = "xper.intent.BAR_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>
    <activity 
        android:name  = "Baz" 
        android:label = "Baz">
        <intent-filter>
            <action 
                android:name = "xper.intent.BAZ_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>

and it executes

    startActivities(
        new Intent[]
        {
            new Intent("xper.intent.FOO_INTENT"),
            new Intent("xper.intent.BAR_INTENT"),
            new Intent("xper.intent.BAZ_INTENT")
        });

then an instance of Baz is created and made the current Activity.

If Baz finishes then an instance of Bar is created and is made the current Activity.

If Bar finishes then an instance of Foo is created and is made the current Activity.

The method documentation states

This method throws ActivityNotFoundException if there was no Activity found for any given Intent. In this case the state of the activity stack is undefined (some Intents in the list may be on it, some not), so you probably want to avoid such situations.

which is not that helpful.

What currently appears to happen in practice is that as long as the ActivityNotFoundException is caught all the Activities corresponding to the Intents before the one that cannot be resolved are started.

There is no mention of whether this method plays nicely with things like the Intent.FLAG_ACTIVITY_CLEAR_TOP flag and other of that ilk should you be sufficiently adventurous to set them in any of the given Intents.

3. Broadcast Intents

The BroadcastReceiver class has acquired an inner class

   PendingResult

and a new method

   public final PendingResult goAsync()

This method makes it possible for a BroadcastReceiver to complete the handling of a broadcast Intent after its onReceive() method has returned. This is done by handing off the returned PendingResult instance to a different thread which can then do the necessary work before using the PendingResult instance to complete the broadcast.

The PendingResult class defines almost the same set of methods for manipulating the broadcast Intent as the BroadcastReceiver class plus the additional method

    public final void finish()

which is used to indicate that the handling of the broadcast Intent has been completed.

The only documentation on this feature appears to be the finish() method documentation, and the PendingResult class
documentation.

The BroadcastReceiver class documentation is otherwise unchanged.

For example

A BroadcastReceiver object is only valid for the duration of the call to onReceive(Context, Intent). Once your code returns from this function, the system considers the object to be finished and no longer active.

which is no longer necessarily true, which is a bit unfortunate.

In the absence of any additional documentation some experiments reveal the following

  • The finish() method works for both dynamically and statically registered BroadcastReceivers.

  • The finish() method works for

    broadcast Intents.

  • The process running a dynamically registered BroadcastReceiver will be killed if the finish() method is not called within approximately ten seconds of the call to the goAsync() method if it is an ordered broadcast, but not otherwise.

  • The process running a statically registered BroadcastReceiver will be killed if the finish() method is not called within approximately ten seconds of the call to the goAsync() method irrespective of the type of the broadcast.

Given the upper limit on the amount of time which can elapse whilest a broadcast Intent is being handled asynchronously it is not clear how useful this feature actually. Presumably somebody out there needs it for something. Either that or somebody added it for a bet.

4. IntentSenders And PendingIntents

The PendingIntent class has acquired a fourth factory method

    public static PendingIntent getActivities(Context context, int requestCode, Intent[] intents, int flags)

Invoking the resulting IntentSender/PendingIntent is equivalent to calling an implementation of the Context.startActivities()
method.

The documentation for this method is a bit confusing. On the one hand it states (emphasis added)

The first Intent in the array is taken as the primary key for the PendingIntent, like the single Intent given to getActivity(Context, int, Intent, int).

and on the other (emphasis added again)

The last intent in the array represents the key for the PendingIntent. In other words, it is the significant element for matching (as done with the single intent given to getActivity(Context, int, Intent, int), its content will be the subject of replacement by send(Context, int, Intent) and FLAG_UPDATE_CURRENT, etc. This is because it is the most specific of the supplied intents, and the UI the user actually sees when the intents are started.

On the basis of some experiments (currently the source code for this is not available) it is the second version which is correct.


Copyright (c) 2011 By Simon Lewis. All Rights Reserved.

February 17, 2011

The Android Intent Based APIs: Part Six – Broadcast Intents

As well as being used in conjunction with Activites and Services Intents can also be used as broadcast Intents.

1. The Broadcast Intent Model

  • Any Application can send a broadcast Intent.

  • To receive broadcast Intents an Application must register a BroadcastReceiver.

  • An Application can register multiple BroadcastReceivers.

  • An Application can register BroadcastReceivers statically and/or dynamically.

  • A BroadcastReceiver can be registered with an associated Intent Filter.

  • The Intent Filter is used to specify which broadcast Intents the BroadcastReceiver wishes to receive.

  • A normal broadcast Intent is sent asynchronously and the ordering of its delivery to the set of BroadcastReceivers eligible to receive it is undefined.

  • An ordered broadcast Intent is delivered sequentially to each member of the set of BroadcastReceivers eligible to receive it in the order defined by the priority of the associated IntentFilters.

  • An ordered broadcast Intent can have additional data associated with it in the form of

    • a code (an int),

    • data (a String), and

    • extras (a Bundle)

  • The initial values of the additional data can be specified by the sender of the ordered broadcast Intent

  • The current values of the additional data can be read and/or replaced by each BroadcastReceiver which receives the ordered broadcast Intent

  • Any BroadcastReceiver which receives an ordered broadcast Intent can stop the sending process

  • The sender of an ordered broadcast Intent can specify that it be notified when the broadcast has completed

  • The sender of an ordered broadcast Intent can access the final values of the additional data when it is notified that the broadcast has completed.

  • A broadcast Intent can be specified to be sticky in which case it will be retained by the system after it has been sent.

  • A sticky broadcast Intent can be removed after it has been sent.

  • BroadcastReceivers dynamically registered after a sticky broadcast Intent has been sent, and not subsequently removed, will still receive it if all other the criteria for receiving it have been met.

  • A sticky broadcast Intent can be retrieved at any time after it has been sent without registering a BroadcastReceiver.

  • An Application can specify a permission when sending a normal or ordered broadcast Intent.

  • A BroadcastReceiver cannot receive a normal or ordered broadcast Intent sent with an associated permission if the Application that registered the BroadcastReceiver has not been granted that permission.

  • An Application can specify a permission when registering a BroadcastReceiver.

  • A BroadcastReceiver registered with an associated permission cannot receive any normal or ordered broadcast Intent sent by an Application which has not been granted that permission.

When used in this way Intents are effectively Events and BroadcastReceivers are Event Handlers/Listeners.

2. The BroadcastReceiver Class

The class

    android.content.BroadcastReceiver

is abstract. To receive broadcast Intents a sub-class must be defined which implements the method

    public abstract void onReceive(Context context, Intent intent)

It is this method which is invoked when a broadcast Intent satisfies the criteria the BroadcastReceiver was registered with.

3. Registering Broadcast Receivers Statically

An Application can declare Broadcast Receivers in its manifest (the AndroidManifest.xml file), by defining one or more

    receiver

elements, as children of the

    application 

element.

3.1 The receiver Element

3.1.1 Attributes

3.1.1.1 The enabled Attribute

The enabled attribute is optional. If present its value can be either “true” or “false”

If the value is “true” then the System can create instances of the BroadcastReceiver and it can function normally.

If the value is “false” then the System cannot create instances of the BroadcastReceiver and it is not functional.

The default value is “true”.

If the Application itself is not enabled then the BroadcastReceiver is not functional.

3.1.1.2 The exported Attribute

The exported attribute is optional. If present its value can be either “true” or “false”.

If the value is “true” then the BroadcastReceiver can, if all other criteria are met, receive broadcast Intents from any Application.

If the value is “false” then the BroadcastReceiver can only receive broadcast Intents from the registering Application or other Applications with the same User Id even if the other specified criteria are met.

The default value is “true” if the registered BroadcastReceiver has one or more IntentFilters associated with it. Otherwise it is “false”.

3.1.1.3 The icon Attribute

The icon attribute is optional. If present it should specify a Drawable resource. This will be used by the System to visually identify the BroadcastReceiver to the User if necessary.

3.1.1.4 The label Attribute

The label attribute is optional. If present it should specify a String resource. This will be used by the System to identify the BroadcastReceiver to the User if necessary.

3.1.1.5 The name Attribute

The name attribute is mandatory. It specifies the name of the BroadcastReceiver class.

The documentation for this attribute states

This should be a fully qualified class name (such as, “com.example.project.ReportReceiver”). However, as a shorthand, if the first character of the name is a period (for example, “. ReportReceiver”), it is appended to the package name specified in the <manifest> element.

Which is true as far as it goes.

By default the Android ADT Eclipse plugin actually generates receiver elements that look like this

    <receiver android:name="XperBroadcastReceiver"></receiver>

The attribute value is simply the unqualified class name and this also works.

3.1.1.6 The permission Attribute

The permission attribute is optional. If present then the BroadcastReceiver cannot receive broadcast Intents sent by Applications which have not been granted the specified permission.

3.1.1.7 The process Attribute

The process attribute is optional. If present it specifies the process in which the Systen should create the BroadcastReceiver when necessary.

3.1.2 Child Elements

The receiver element can have two child elements

    intent-filter

and

    meta-data

Both elements are optional. If present both elements can occur multiple times.

4. Registering And Unregistering BroadcastReceivers Dynamically

4.1 Registration

A BroadcastReceiver can be registered dynamically using an implementation of the android.content.Context

    public abstract Intent registerReceiver(
                               BroadcastReceiver receiver, 
                               IntentFilter      filter, 
                               String            broadcastPermission, 
                               Handler           scheduler)

method.

If the broadcastPermission argument is non-null then the registered BroadcastReceiver cannot receive broadcast Intents from any Application which has not been granted the specified permission.

If the scheduler argument is non-null then the registered BroadcastReceiver’s onReceive() method will be executed in the context of the specified Handler.

If it is not necessary to specify either a permission or a Handler then an implementation of the
android.content.Context

    public abstract Intent registerReceiver(
                               BroadcastReceiver receiver,  
                               IntentFilter      filter)

method can be used instead.

Both methods will return either a broadcast Intent which was sent in sticky mode which matches the given IntentFilter, or null. (See also).

Both methods can also be used to access a stick broadcast Intent directly.

The same BroadcastReceiver can be registered multiple times with different IntentFilters.

4.2 Unregistration

A dynamically registered BroadcastReceiver can be unregistered using an implementation of the android.content.Context

    public abstract void unregisterReceiver(BroadcastReceiver receiver)

The effect of this method is undo the effects of all calls to either of the registerReceiver() methods used to register the given BroadcastReceiver.

5. BroadcastReceiver Lifecycles

The lifecycle of a BroadcastReceiver differs depending upon whether it was registered statically or dynamically.

5.1 The Static BroadcastReceiver Lifecycle

The lifecycle of a statically registered BroadcastReceiver is under the control of the System.

When a broadcast Intent is to be delivered to a statically registered BroadcastReceiver the System will

  1. create the appropriate process in which to run it if necessary

  2. create an instance of the BroadcastReceiver and invoke its onReceive() method

Once the onReceive() method has returned the System may stop the process used to run it.

The transient nature of a statically registered BroadcastReceiver means that its onReceive() method cannot use any functionality which is asynchronous, for example, binding to a Service.

This constraint is enforced by the implementation of the Context passed to the method at runtime. For example, its implementation of the bindService() method throws a RuntimeException.

5.2 The Dynamic BroadcastReceiver Lifecycle

The lifecycle of a dyamically registered BroadcastReceiver is under the control of the Application.

An Application can create BroadcastReceivers and register and unregister them as and when it chooses.

There are no constraints on the functionality that can be used by the implementation of the onReceive() method of a dynamically registered BroadcastReceiver.

6. BroadcastReceivers And Intent Resolution

If an Intent explicitly specifies a component then the Intent resolves to that Component if it is a BroadcastReceiver. If the specified Component is not a BroadcastReceiver it is equivalent to the case where the Intent cannot be resolved to any BroadcastReceiver(s).

Otherwise a search is made for all BroadcastReceivers with an associated IntentFilter which matches the given Intent, as defined by the IntentFilter.match() method.

If the Intent specifies a package then the search is confined to the Services in that Application package.

An Application can determine the BroadcastReceivers to which a given Intent resolves by using an implementation od the android.content.pm.PackageManager

    public abstract List queryBroadcastReceivers (Intent intent, int flags)

The list is sorted in order from high to low priority as defined by the associated IntentFilters.

7. Broadcasting

A normal broadcast Intent can be sent using an implementation of the android.content.Context

    public abstract void sendBroadcast(Intent intent, String receiverPermission)

method.

If the receiverPermission argument is non-null then a BroadcastReceiver cannot receive the broadcast Intent being sent unless it was declared by an Application which has been granted the given permission.

The method is asynchronous. It returns immediately, and the delivery of the broadcast Intent to the set of eligible BroadcastReceivers executes independently of the method’s caller.

If it is not necessary to specify a permission then an implementation of the android.content.Context

    public abstract void sendBroadcast(Intent intent)

can be used instead.

8. Ordered Broadcasting

8.1 Sending An Ordered Broadcast Intent

A simple ordered broadcast Intent can be sent using an implementation of the android.content.Context

    public abstract void sendOrderedBroadcast(
                             Intent intent, 
                             String receiverPermission)

method.

If the receiverPermission argument is non-null then a BroadcastReceiver cannot receive the broadcast Intent being sent unless it was declared by an Application which has been granted the given permission.

The method is asynchronous. It returns immediately, and the process of sending the ordered broadcast Intent executes independently of the method’s caller.

8.2 Sending An Ordered Broadcast Intent And Getting A “Result”

An implementation of the android.content.Context method

    public abstract void sendOrderedBroadcast(
                             Intent            intent, 
                             String            receiverPermission, 
                             BroadcastReceiver resultReceiver, 
                             Handler           scheduler, 
                             int               initialCode, 
                             String            initialData, 
                             Bundle            initialExtras)

can be used to send an ordered broadcast Intent with associated data and obtain a result.

If the receiverPermission argument is non-null then the broadcast Intent being sent cannot be received by any BroadcastReceiver registered by an Application which has not been granted the specified permission.

If the resultReceiver argument is non-null it specifies a BroadcastReceiver whose onReceive() method will be invoked when the sending of the ordered broadcast Intent completes.

If the scheduler argument is non-null and the resultReceiver argument is also non-null then the onReceive() method of the BroadcastReceiver will be run in the context of the specified Handler.

The

  • initialCode

  • initialData

  • initialExtras

arguments specify the initial values of the

  • code

  • data

  • extras

elements respectively of the additional data associated with the sending of the ordered broadcast Intent.

The method is asynchronous. It returns immediately, and the process of sending the ordered broadcast Intent executes independently of the method’s caller.

8.2.1 BroadcastReceivers And Ordered Broadcast Intents

The BroadcastReceiver class defines a number of methods related to ordered broadcast Intents which can be used by an implementation of the onReceive() method.

8.2.1.1 Determining The “Type” Of A Broadcast Intent

The

    public final boolean isOrderedBroadcast()

method will return true if the onReceive()method has been invoked with an ordered broadcast Intent.

8.2.1.2 Getting The Ordered Broadcast Intent “Result” Data

The methods

  • public final int getResultCode()

  • public final String getResultData()

  • public final Bundle getResultExtras(boolean makeMap)</p

will return the current values of the

  • code

  • data

  • extras

respectively.

If the code is not defined then the default value is -1.

If the data is not defined then the default value is null.

If the extras are not defined then the default value is null unless the makeMap argument is true in which case an empty Bundle is created and returned.

These methods can be called when the onReceive() method was not invoked on an ordered broadcast Intent and they will return the default values as above.

8.2.1.3 Setting The Ordered Broadcast Intent “Result” Data

The methods

  • public final void setResultCode(int code)

  • public final void setResultData(String data)

  • public final void setResultExtras (Bundle extras)

will set the current values of the

  • code

  • data

  • extras

respectively.

Alternatively all three can be set simultaneously using the

    public final void setResult(int code, String data, Bundle extras)

method.

Using any of these methods when the onReceive() method has not been invoked on an ordered broadcast Intent will result in a RuntimeException, except in one situation.

8.2.1.4 Stopping The Process Of Sending Of An Ordered Broadcast Intent

A BroadcastReceiver can stop the process of sending an ordered broadcast Intent by calling the

    public final void abortBroadcast()

method.

Any BroadcastReceivers of the same priority that have not already received the Intent and all those with a lower priority than the current BroacastReceiver will not receive the Intent.

Using this method when the onReceive() method has not been invoked on an ordered broadcast Intent will result in a RuntimeException, except in one situation.

8.3 Ordered Broadcast Intent Examples

There are four BroadcastReceivers registered statically as follows

    <receiver 
        android:name = "OrderedBroadcastReceiverOne">
        <intent-filter 
            android:priority = "1">
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT"/>
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT_ONE"/>
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT_TWO"/>
        </intent-filter>
    </receiver>
	
    <receiver 
        android:name = "OrderedBroadcastReceiverTwoA">
        <intent-filter 
            android:priority = "2">
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT"/>
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT_ONE"/>
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT_TWO"/>
        </intent-filter>
    </receiver>
	
    <receiver 
        android:name = "OrderedBroadcastReceiverTwoB">
        <intent-filter 
            android:priority = "2">
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT"/>
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT_ONE"/>
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT_TWO"/>
        </intent-filter>
    </receiver>
   
    <receiver 
        android:name = "OrderedBroadcastReceiverThree">
        <intent-filter 
            android:priority = "3">
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT"/>
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT_ONE"/>
            <action 
                android:name = "xper.example.ORDERED_BROADCAST_INTENT_TWO"/>
            </intent-filter>
    </receiver>

Their respective onReceive() methods are defined as follows

    // OrderedBroadcastReceiverOne.onReceive()

    public void onReceive(Context context, Intent intent) 
    {
        System.out.println("One: " + intent);
        System.out.println("\tgetResultCode() => " + getResultCode());
        System.out.println("\tgetResultData() => " + getResultData());
        setResultCode(getResultCode() + 1);
        setResultData(getResultData() + ", One");
		
        Bundle extras = getResultExtras(true);
		   
        extras.putString("One.value", "One");
        setResultExtras(extras);
        System.out.println("One Done");
    }
	
    ...
	
    // OrderedBroadcastReceiverTwoA.onReceive()
	 
    public void onReceive(Context context, Intent intent) 
    {
        System.out.println("TwoA");
        System.out.println(intent);
        System.out.println("\tgetResultCode() => " + getResultCode());
        System.out.println("\tgetResultData() => " + getResultData());
        setResultCode(getResultCode() + 20);
        setResultData(getResultData() + ", TwoA");
	   
        Bundle extras = getResultExtras(true);
	   
        extras.putString("TwoA.value", "TwoA");
        setResultExtras(extras);
        if ("xper.example.ORDERED_BROADCAST_INTENT_TWO".equals(intent.getAction())) 
        {
            abortBroadcast();
        }
        System.out.println("TwoA done");
    }
	
    ...
	
    // OrderedBroadcastReceiverTwoB.onReceive()
	
    public void onReceive(Context context, Intent intent) 
    {
        System.out.println("TwoB");
        System.out.println(intent);
        System.out.println("\tgetResultCode() => " + getResultCode());
        System.out.println("\tgetResultData() => " + getResultData());
        setResultCode(getResultCode() + 20);
        setResultData(getResultData() + ", TwoB");
		
        Bundle extras = getResultExtras(true);
		   
        extras.putString("TwoB.value", "TwoB");
        setResultExtras(extras);
        if ("xper.example.ORDERED_BROADCAST_INTENT_TWO".equals(intent.getAction())) 
        {
            abortBroadcast();
        }
        System.out.println("TwoB done");
    }
	
    ...
	
    // OrderedBroadcastReceiverThree.onReceive()
	
    public void onReceive(Context context, Intent intent) 
    {
        System.out.println("Three");
        System.out.println(intent);
        System.out.println("\tgetResultCode() => " + getResultCode());
        System.out.println("\tgetResultData() => " + getResultData());
        setResultCode(getResultCode() + 300);
        setResultData(getResultData() + ", Three");
		
        Bundle extras = getResultExtras(true);
		   
        extras.putString("Three.value", "Three");
        setResultExtras(extras);
        System.out.println("Three done");
   }

There is a class ResultReceiver which is a sub-class of the class BroadcastReceiver.
Its onReceive() method is defined as follows.

    public void onReceive(Context context, Intent intent) 
    {
        System.out.println("ResultReceiver");
        System.out.println(intent);
        System.out.println("\tgetResultCode() => " + getResultCode());
        System.out.println("\tgetResultData() => " + getResultData());
	    
        Bundle extras = getResultExtras(true);
	    
        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 done");
    }

8.3.1 Example One

Sending an ordered broadcast Intent which is handled by all the registered BroadcastReceivers.

This example illustrates the delivery of the broadcast Intent to the BroadcastReceivers in priority order, and the use of result data

8.3.1.1 Stage One: Sending The Intent

The Intent is sent as follows

    sendOrderedBroadcast(
        new Intent(
            "xper.example.ORDERED_BROADCAST_INTENT"), 
        null, 
        new ResultReceiver(), 
        null, 
        0, 
        "ExampleOne", 
        null);

8.3.1.2 Stage Two: The Broadcast Intent Is Received By OrderedBroadcastReceiverThree

The OrderedBroadcastReceiverThree.onReceive() method prints

    Three
    Intent { act=xper.example.ORDERED_BROADCAST_INTENT cmp=xper.example.three/.OrderedBroadcastReceiverThree }
        getResultCode() => 0
        getResultData() => ExampleOne
    Three done

8.3.1.3 Stage Three: The Broadcast Intent Is Received By OrderedBroadcastReceiverTwoA

The OrderedBroadcastReceiverTwoA.onReceive() method prints

    TwoA
    Intent { act=xper.example.ORDERED_BROADCAST_INTENT cmp=xper.example.two.a/.OrderedBroadcastReceiverTwoA }
        getResultCode() => 300
        getResultData() => ExampleOne, Three
    TwoA done

8.3.1.4 Stage Four: The Broadcast Intent Is Received By OrderedBroadcastReceiverTwoB

The OrderedBroadcastReceiverTwoB.onReceive() method prints

    TwoB
    Intent { act=xper.example.ORDERED_BROADCAST_INTENT cmp=xper.example.two.b/.OrderedBroadcastReceiverTwoB }
        getResultCode() => 320
        getResultData() => ExampleOne, Three, TwoA
    TwoB done

8.3.1.5 Stage Five: The Broadcast Intent Is Received By OrderedBroadcastReceiverOne

The OrderedBroadcastReceiverOne.onReceive() method prints

    One: Intent { act=xper.example.ORDERED_BROADCAST_INTENT cmp=xper.example.one/.OrderedBroadcastReceiverOne }
        getResultCode() => 340
        getResultData() => ExampleOne, Three, TwoA, TwoB
    One Done

8.3.1.6 Stage Six: ResultReceiver onReceive() Method Called

The ResultReceiver.onReceive() method prints

    ResultReceiver
    Intent { act=xper.example.ORDERED_BROADCAST_INTENT }
        getResultCode() => 341
        getResultData() => ExampleOne, Three, TwoA, TwoB, One
        Begin Extras
            Three.value	=> Three
            TwoB.value	=> TwoB
            TwoA.value	=> TwoA
            One.value	=> One
        End Extras
    ResultReceiver done

8.3.2 Example Two

Sending an ordered broadcast Intent which is handled by all the registered BroadcastRecivers but one of them stops the sending process.

8.3.2.1 Stage One: Sending The Intent

The Intent is sent as follows

    sendOrderedBroadcast(
        new Intent(
            "xper.example.ORDERED_BROADCAST_INTENT_TWO"), 
        null, 
        new ResultReceiver(), 
        null, 
        0, 
        "ExampleTwo", 
        null);

8.3.2.2 Stage Two: The Broadcast Intent Is Received By OrderedBroadcastReceiverThree

The OrderedBroadcastReceiverThree.onReceive() method prints

    Three
    Intent { act=xper.example.ORDERED_BROADCAST_INTENT_TWO cmp=xper.example.three/.OrderedBroadcastReceiverThree }
        getResultCode() => 0
        getResultData() => ExampleTwo
    Three done

8.3.2.3 Stage Three: The Broadcast Intent Is Received By OrderedBroadcastReceiverTwoA

The OrderedBroadcastReceiverTwoA.onReceive() method prints

    TwoA
    Intent { act=xper.example.ORDERED_BROADCAST_INTENT_TWO cmp=xper.example.two.a/.OrderedBroadcastReceiverTwoA }
        getResultCode() => 300
        getResultData() => ExampleTwo, Three
    TwoA done

and stops the sending of the broadcast Intent.

8.3.2.4 Stage Four: ResultReceiver onReceive() Method Called

The ResultReceiver.onReceive() method prints

    ResultReceiver
    Intent { act=xper.example.ORDERED_BROADCAST_INTENT_TWO }
        getResultCode() => 320
        getResultData() => ExampleTwo, Three, TwoA
        Begin Extras
            Three.value	=> Three
             TwoA.value	=> TwoA
        End Extras
    ResultReceiver done

8.3.3 Example Three

Sending an ordered broadcast Intent which is not handled by any of the registered BroadcastReecivers.

8.3.3.1 Stage One: Sending The Intent

The Intent is sent as follows

    sendOrderedBroadcast(
        new Intent(
            "xper.example.ORDERED_BROADCAST_INTENT_FOUR"), 
	    null, 
	    new ResultReceiver(), 
	    null, 
	    -3, 
	    "ExampleThree", 
	    null);

8.3.3.2 Stage Two: ResultReceiver onReceive() Method Called

The ResultReceiver.onReceive() method prints

    ResultReceiver
    Intent { act=xper.example.ORDERED_BROADCAST_INTENT_FOUR }
        getResultCode() => -3
        getResultData() => ExampleThree
        Begin Extras
        End Extras
    ResultReceiver done

9. Sticky Broadcast Intents

Both normal and ordered broadcasts can also be performed in sticky mode.

To send a broadcast Intent in sticky mode an Application must have been granted the BROADCAST_STICKY permission.

9.1 Sending A Sticky Broadcast Intent

A normal broadcast Intent can be sent in sticky mode using an implementation of the android.content.Context

    public abstract void sendStickyBroadcast(Intent intent)

method.

This works in the same way as the short-form method for sending a normal broadcast Intent.

9.2 Sending A Sticky Ordered Broadcast Intent

An ordered broadcast Intent can be sent in sticky mode using an implementation of the android.content.Context

    public abstract void sendStickyOrderedBroadcast(
                             Intent            intent, 
                             BroadcastReceiver resultReceiver, 
                             Handler           scheduler, 
                             int               initialCode, 
                             String            initialData, 
                             Bundle            initialExtras)

method.

This works in the same way as the long-form method for sending an ordered broadcast Intent.

9.3 Sticky Broadcast Intent “Replacement”

If when a broadcast Intent is sent in sticky mode it is found to match a sticky broadcast Intent sent previously then it will replace
the existing one. The Intent.filterEquals() method is used to determine whether Intents match.

One implication of this is that there can be multiple sticky broadcast Intents with, for example, the same action but different
data URIs, since these will not match.

Conversely, broadcast Intents that differ only in their extras will match.

Note also that an ordered broadcast Intent sent in sticky mode can replace a normal broadcast Intent sent in sticky mode, and vice-versa.

For example, the following


    IntentFilter f = new IntentFilter("xper.sticky.BROADCAST_INTENT");
        
    sendStickyBroadcast(
        new Intent(
                "xper.sticky.BROADCAST_INTENT").
            putExtra(
                "Type", 
                "Normal"));
    System.out.println(registerReceiver(null, f).getStringExtra("Type"));
    sendStickyOrderedBroadcast(
        new Intent(
                "xper.sticky.BROADCAST_INTENT").
            putExtra(
                "Type", 
                "Ordered"), 
        null, 
        null, 
        0, 
        null, 
        null);
    System.out.println(registerReceiver(null, f).getStringExtra("Type"));
    sendStickyBroadcast(
        new Intent(
                "xper.sticky.BROADCAST_INTENT").
            putExtra(
                "Type", 
                "Normal")); 
    System.out.println(registerReceiver(null, f).getStringExtra("Type"));

will print


    Normal
    Ordered
    Normal

9.4 Accessing A Sticky Broadcast Intent Directly

Both registerReceiver() methods can be passed a receiver argument of null. As in the non-null receiver argument case if one or more sticky broadcast Intents match the supplied IntentFilter then one of them will be returned from the method.

For example, although it is not documented as such the broadcast Intent with the action

    android.net.conn.CONNECTIVITY_CHANGE

is sent in sticky mode and hence is accesible in this way.

On the emulator the following code

    IntentFilter f = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
    Intent       i = registerReceiver(null, f);
        
    System.out.println(i);
        
    Bundle b = i.getExtras();
        
    for (String key : b.keySet())
    {
        System.out.print(key);
        System.out.print(" => ");
        System.out.println(b.get(key));
    }

prints

    Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x10000000 (has extras) }
    networkInfo => NetworkInfo: type: mobile[UMTS], state: CONNECTED/CONNECTED, reason: simLoaded, ... [elided]
    reason => simLoaded
    extraInfo => internet
    inetCondition => 0

Although it is possible to use the long-form of the registerReceiver() method in this way there is effectively no point since the permission argument has no effect.

For example, replacing the line

   Intent i = registerReceiver(null, f);

in the example above with

   Intent i = registerReceiver(null, f, "xper.permission.NO_SUCH_PERMISSION", null);

does not change the behaviour at all.

9.5 Removing A Sticky Broadcast Intent

A sticky broadcast Intent can be removed by calling an implementation of the android.content.Context method

    public abstract void removeStickyBroadcast(Intent intent)

To remove a sticky broadcast Intent an Application must have been granted the BROADCAST_STICKY permission.

The method will remove the sticky broadcast Intent, if any, which matches, as determined by the Intent.filterEquals() method, the Intent passed as the intent argument.

9.6 Sticky Broadcast Intents And Dynamically Registered BroadcastReceivers

9.6.1 Registration

When a BroadcastReceiver is registered dynamically using the short-form registerReceiver() method, then, if the associated IntentFilter matches one or more sticky broadcast Intents

  • one of the matching Intents will be returned by the method, and then, at some point

  • the BroadcastReceiver’s onReceive() method will be invoked for each matching sticky broadcast Intent

For example, if we define the class StickyBroadcastReceiver as follows

    public class StickyBroadcastReceiver 
                 extends 
                     BroadcastReceiver 
    {
        @Override
        public void onReceive(Context context, Intent intent) 
        {
            System.out.println("StickyBroadcastReceiver.onReceive(...)");
            System.out.println(intent);
            System.out.println("\tisInitialStickyBroadcast() => " + isInitialStickyBroadcast());
            System.out.println("StickyBroadcastReceiver.onReceive(...) done");
        }
    }

then the following code

    Intent i = registerReceiver(
                   new StickyBroadcastReceiver(), 
                   new IntentFilter(
                       ConnectivityManager.CONNECTIVITY_ACTION));
    	
    System.out.println("registerReceiver() => " + i);

prints

    registerReceiver() => Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x10000000 (has extras) }

and then the onReceive() method prints

    StickyBroadcastReceiver.onReceive(...)
    Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x10000000 (has extras) }
        isInitialStickyBroadcast() => true
    StickyBroadcastReceiver.onReceive(...) done

When a BroadcastReceiver is registered dynamically using the long-form registerReceiver() method then, if the associated IntentFilter matches one or more sticky broadcast Intents, the exact behaviour depends on whether or not a permission is specified.

The method will return one of the matching Intents irrespective of whether or not a permission was specified.

However, once the method has returned, if a permission was specified, then the BroadcastReceiver’s onReceive() method will not be invoked on any sticky broadcast Intent sent by an Application which has not been granted that permission.

For example, modifying the previous example, then the following code

 Intent i = registerReceiver(
                new StickyBroadcastReceiver(), 
                new IntentFilter(
                    ConnectivityManager.CONNECTIVITY_ACTION),
                "xper.permission.NO_SUCH_PERMISSION",
                null);

prints

    registerReceiver() => Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x10000000 (has extras) }

and that is it. The onReceive() method is not invoked.

The behaviour with respect to the onReceive() method is consistent with the way permissions work, it is really the behaviour of the registerReceiver() method which is anomalous.

9.6.2 The onReceive() Method

A dynamically registered BroadcastReceiver can determine whether it is being invoked on a sticky broadcast Intent by calling the

    public final boolean isInitialStickyBroadcast()

method.

If it is then the behaviour of some of the methods defined by the BroadcastReceiver class for use with ordered broadcast Intents is slightly different.

  • The isOrderedBroadcast() method always returns false

  • The methods for setting result data do not throw RuntimeExceptions, they are simply no-ops

  • The abortBroadcast() method does not throw a RuntimeException, it is simply a no-op

10. Broadcast Intents And Intent Flags

There are two Intent class constants which define flags specifically for use with broadcast Intents.

10.1 FLAG_RECEIVER_REGISTERED_ONLY

If this flag is set in a broadcast Intent then it will only be delivered to those eligible BroadcastReceivers which were dynamically registered.

10.2 FLAG_RECEIVER_REPLACE_PENDING

If this flag is set in a broadcast Intent then it will replace any broadcast Intent which matches it, as defined by Intent.filterEquals(), which is currently in the process of being delivered to any eligible BroadcastReceivers.

This effect is not atomic. Some BroadcastReceivers may receive both the original and the replacement broadcast Intent, others only the replacement, as the following rather contrived example demonstrates.

We define two static BroadcastReceivers

    <receiver 
        android:name="Sole">
        <intent-filter>
            <action 
                android:name="xper.receiver.intent.RECEIVER_SOLE_INTENT"/>
            <action 
                android:name="xper.receiver.intent.RECEIVER_SEA_AREA_INTENT"/>
        </intent-filter>
    </receiver>

    <receiver 
        android:name="Fastnet">
        <intent-filter>
            <action 
                android:name="xper.receiver.intent.RECEIVER_FASTNET_INTENT"/>
            <action 
                android:name="xper.receiver.intent.RECEIVER_SEA_AREA_INTENT"/>
        </intent-filter>
    </receiver>

each in a separate Application, and one dynamic BroadcastReceiver registered by a third Application as follows

    IntentFilter f = new IntentFilter();
        
    f.addAction("xper.receiver.intent.RECEIVER_LUNDY_INTENT");
    f.addAction("xper.receiver.intent.RECEIVER_SEA_AREA_INTENT");
    registerReceiver(new Lundy(), f);

We define their respective onReceive() methods to be

    // Sole

    public void onReceive(Context context, Intent intent) 
    {
        System.out.println("Sole.onReceive(..., " + intent + ")");
        System.out.println("Sole.onReceive(...) N == " + intent.getStringExtra("N"));
    }
	
    ...

    // Fastnet
	
    public void onReceive(Context context, Intent intent)
    {
        System.out.println("Fastnet.onReceive(..., " + intent + ")");
        System.out.println("Fastnet.onReceive(...) N == " + intent.getStringExtra("N"));
    }
	
    ...
	
    // Lundy
	
    public void onReceive(Context context, Intent intent) 
    {
        System.out.println("Lundy.onReceive(..., " + intent + ")");
        System.out.println("Lundy.onReceive(...) N == " + intent.getStringExtra("N"));
    }

If we execute the following

    sendBroadcast(
        new Intent(
                "xper.receiver.intent.RECEIVER_SEA_AREA_INTENT").
            setFlags(
                Intent.FLAG_RECEIVER_REPLACE_PENDING).
            putExtra(
                "N", 
                "One"));
    sendBroadcast(
        new Intent(
                "xper.receiver.intent.RECEIVER_SEA_AREA_INTENT").
            setFlags(
                Intent.FLAG_RECEIVER_REPLACE_PENDING).
            putExtra(
                "N", 
                "Two"));
    sendBroadcast(
        new Intent(
                "xper.receiver.intent.RECEIVER_SEA_AREA_INTENT").
            setFlags(
                Intent.FLAG_RECEIVER_REPLACE_PENDING).
            putExtra(
                "N", 
                "Three"));

then we get (output slightly reformatted)

    Lundy.onReceive(..., Intent { act=xper.receiver.intent.RECEIVER_SEA_AREA_INTENT flg=0x20000000 (has extras) })
    Lundy.onReceive(...) N == One

    Fastnet.onReceive(..., Intent { act=xper.receiver.intent.RECEIVER_SEA_AREA_INTENT \ 
        flg=0x20000000 cmp=xper.receiver.fastnet/.Fastnet (has extras) })
    Fastnet.onReceive(...) N == One

    Lundy.onReceive(..., Intent { act=xper.receiver.intent.RECEIVER_SEA_AREA_INTENT flg=0x20000000 (has extras) })
    Lundy.onReceive(...) N == Two

    Lundy.onReceive(..., Intent { act=xper.receiver.intent.RECEIVER_SEA_AREA_INTENT flg=0x20000000 (has extras) })
    Lundy.onReceive(...) N == Three

    Sole.onReceive(..., Intent { act=xper.receiver.intent.RECEIVER_SEA_AREA_INTENT \
        flg=0x20000000 cmp=xper.receiver.sole/.Sole (has extras) })
    Sole.onReceive(...) N == One

    Fastnet.onReceive(..., Intent { act=xper.receiver.intent.RECEIVER_SEA_AREA_INTENT \  
        flg=0x20000000 cmp=xper.receiver.fastnet/.Fastnet (has extras) })
    Fastnet.onReceive(...) N == Three

    Sole.onReceive(..., Intent { act=xper.receiver.intent.RECEIVER_SEA_AREA_INTENT \
        flg=0x20000000 cmp=xper.receiver.sole/.Sole (has extras) })
    Sole.onReceive(...) N == Three

11. Sending A Broadcast Intent To A Specific BroadcastReceiver

Both normal and ordered broadcast Intents can be sent to a specific BroadcastReceiver by setting the broadcast Intent’s component explicitly.

For example, assuming the Application package is

    xper.specific

and the BroadcastReceiver is declared as follows

    <receiver 
        android:name = "SpecificBroadcastReceiver">
        <intent-filter>
            <action android:name = "xper.specific.SPECIFIC_BROADCAST_INTENT"/>
        </intent-filter>
    </receiver>

then a normal broadcast Intent can be sent to it as follows.

    sendBroadcast(
        new Intent(
                "xper.specific.BROADCAST_INTENT").
            setClassName(
                "xper.specific", 
                "xper.specific.SpecificBroadcastReceiver"));

Note that, as in this example, the Intent does not have to match the IntentFilter(s) associated with the BroadcastReceiver, which has some interesting implications.

Although specifying the BroadcastReceiver explicitly when sending the broadcast Intent overrides the BroadcastReceiver’s IntentFilter(s) both sender and/or receiver permissions, if specified, still apply.

It is not possible to send a broadcast Intent to a specific BroadcastReceiver in sticky mode. Attempting to do so results in a SecurityException.

12. Anonymous BroadcastReceivers

It is possible to statically register a BroadcastReceiver without any IntentFilters. For example.

    <receiver 
        android:name = "AnonymousBroadcastReceiver"/>

Broadcast Intents can still be sent to it by specifying the BroadcastReceiver explicitly.

In this can it can only be done from the registering Application since in the BroadcastReceiver has not been exported.

It is of course possible to export it as well

    <receiver 
        android:name     = "AnonymousBroadcastReceiver" 
        android:exported = "true"/>

Copyright (c) 2011 By Simon Lewis. All Rights Reserved.

Create a free website or blog at WordPress.com.

%d bloggers like this: