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.

Advertisements

Blog at WordPress.com.

%d bloggers like this: