Just An Application

December 12, 2010

The Android Intent Based APIs: Part Two – The Intent class

Filed under: Android, Intents, Java, Mobile Java — Tags: , , , — Simon Lewis @ 11:50 am

An Intent is represented by an instance of the class

    android.content.Intent

1. Construction

The Intent class defines no less than six constructors.

  • Intent()

  • Intent(Intent o)

  • Intent(String action)

  • Intent(String action, Uri uri)

  • Intent(Context packageContext, Class cls)

  • Intent(String action, Uri uri, Context packageContext, Class cls)

Intent instances are mutable so only the default constructor is actually necessary. The other five are effectively convenience constructors. The same effect could be achieved by using the default constructor and setting the other values explicitly.

Note also that it is not possible to construct an Intent with a specific data type, or set of categories or flags, or with extras. The Intent must be created first and then the data type and/or categories and/or flags and/or extras set using the appropriate accessors.

2. Constituent Parts

2.1 Action

An Intent’s action is represented by a String which specifies a qualified name.

An Intent can be constructed with a specific action, using either this, this, or this, or the action can be set explicitly using the method

    public Intent setAction(String action)

2.2 Data And Data Type

An Intent’s data is represented by an instance of the class

    android.net.Uri

An Intent’s data type is a MIME type which is represented by a String of the form

    <type> '/' <sub-type>

as per RFC 2045, at least that is the theory. In practice, not so much, as there is no validation being done, at least not at the point the data type is set.

Note that both URI and MIME type comparison is case-sensitive in the context of Intents.

2.2.1 Setting

An Intent’s data URI can be set using the method

    public Intent setData(Uri data)

If the scheme of the data URI is

    content 

then the MIME type of the data specified is derived from the URI.

For any other scheme the data type is null by default.

The data URI can be set in conjunction with an explict type using the method

    public Intent setDataAndType(Uri data, String type)

Conversely an Intent’s data type can be set without setting its data URI using the method

    public Intent setType(String type)

This method and the setData(Uri) method are mutually exclusive. Setting the type will unset the data URI and vice-versa.

2.2.2 Getting

The method

    public Uri getData()

returns the Intent’s data URI if any.

There is also a convenience method

    public Uri getDataString()

which returns the data URI as an encoded String, as well as one

    public String getScheme()

which returns the scheme, if any, of the data URI.

The method

    public String getType()

will return the Intent’s data type, but if and only if it was set explicitly using either the method setDataAndType() or the method setType().

2.3 Categories

An Intent’s categories are represented by a Set of Strings, each String being a qualified name

A category can be added to the Intent’s set of categories using the method

    public Intent addCategory(String category)

or removed using the method

    public void removeCategory(String category)

The method

    public Set<String> getCategories()

can be used to get the set itself, and strangely enough the return value is the actual set held by the Intent.

The method documentation contains the admonishment

Do not modify!

which is not quite as effective as simply not returning the actual set in the first place.

2.4 Flags

An Intent’s flags are represented by specific bits set within an integer value.

An Intent’s flags can be set using the method

    public Intent setFlags(int flags)

Alternatively a flag can be added to the Intent’s current set of flags using the method

    public Intent addFlags(int flags)

2.5 Extras

There are a plethora of methods for setting, getting, replacing, and removing an Intent’s extras.

2.5.1 Setting

Setting the value of an extra can be done using a method of the form


    public Intent putExtra(String key, Type value);

where Type can be any one of the following.

  • Bundle

  • CharSequence

  • Parcelable

  • Parcelable[]

  • Serializable

  • String

  • String[]

  • boolean

  • boolean[]

  • byte

  • byte[]

  • char

  • char[]

  • double

  • double[]

  • float

  • float[]

  • int

  • int[]

  • long

  • long[]
  • short

  • short[]

As if this was not sufficient there are also methods of the form


    public Intent putTypeArrayListExtra(String key, ArrayList<? extends Type> value);

where Type can be any one of the following

  • CharSequence

  • Integer

  • Parcelable

  • String

And for good measure it also possible to set multiple extras simultaneously using either the method


   public Intent putExtras(Bundle extras)

or the method


   public Intent putExtras(Intent src)

2.5.2 Getting

There are corresponding methods for getting the value of an extra.

If the extra is an object type then the corresponding method is of the form.


    public Type getTypeExtra(String name)

If there is no extra of the given name the method will return null.

If the extra is an array type then the corresponding method is of the form.


    public Type[] getTypeArrayExtra(String name)

If there is no extra of the given name the method will return null.

If the extra is a primitive type then the corresponding method is of the form.


    public Type getTypeExtra(String name, Type defaultValue)

If there is no extra of the given name the method will return the supplied default value.

All of an Intent’s extras can be obtained as a Bundle by calling the method


    public Bundle getExtras ()

2.5.3 Replacing And Removing

It is possible to replace all of an Intent’s extras with those contained in a given Bundle using the method


    public Intent replaceExtras(Bundle extras)

or with all the extras from another Intent using the method


    public Intent replaceExtras(Intent src)

Passing null to the first method or an Intent with no extras to the second will result in the removal of all the Intent’s extras.

A specific extra can be removed from an Intent using the method


    public void removeExtra (String name)

2.6 Component

An Intent can be constructed with the name of the Component to which it should be sent specified explicitly, see this, or this, or it can be set explicitly using one of the following methods.

  • public Intent setClass(Context packageContext, Class<?> cls)

  • public Intent setClassName(Context packageContext, String className)

  • public Intent setClassName(String packageContext, String className)

  • public Intent setComponent(ComponentName component)

The first three methods are convenience methods. They are simply wrappers around the corresponding ComponentName constructors.

2.7 Package

It is possible to constrain the set of Components to which an Intent can be sent using the method

    public Intent setPackage(String packageName)

The argument specifies the name of the Application package to search for Components to which the Intent should be sent.

3. Cloning And Copying

An Intent can be copied using the copy constructor, or it can be cloned using the

    public Object clone() 

method, which amounts to the same thing as it simply calls the copy constructor.

There is also a

    public Intent cloneFilter() 

method, which, in the words of the method documentation, makes

a clone of only the parts of the Intent that are relevant for filter matching: the action, data, type, component, and categories.

In practice what this amounts to is that the resulting Intent does not contain either the flags or the extras from the original Intent.

There is also a more elaborate method

    public int fillIn(Intent other, int flags) 

which can be used to selectively copy parts of another Intent.

The flags specified by the the following class constants can be bitwise or-ed and passed as the flags argument.

  • FILL_IN_ACTION

  • FILL_IN_CATEGORIES

  • FILL_IN_COMPONENT

  • FILL_IN_DATA

  • FILL_IN_PACKAGE

The basic principle of operation is that the value of a constituent part in the other Intent will be copied if it is not null, and

  • the corresponding part in the Intent on which the method is called is null, or

  • the flag corresponding to that part is set in the flags argument.

To make life more interesting this is not true for the Intent’s flags which are bitwise or-ed together come what may, or the component which will only be copied if the corresponding flag, FILL_IN_COMPONENT in this case, is set in the flags argument.

The return value is a set of flags identifying which parts were actually set.

4. Equality

The Intent class does not override the

    public boolean equals(Object o)

and

    public int hashCode()

methods, so using Intents as keys in a Hashtable, for example, is not a good idea.

Instead it defines the methods

    public boolean filterEquals(Object other)

and

    public int filterHashCode()

The methods are documented as using only the

action, data, type, class, and categories

for the purposes of testing for equality, or computing the hash code, but in each case this is not entirely true. Each method also acts upon the component and package parts if present.

5. URIs

One of the more arcane pieces of functionality supported by Intents is the ability to turn an Intent into a URI and vice-versa.

It is left as an exercise for the reader to identify those places, if any, in the system where this functionality is actually used.

5.1 Converting An Intent to a URI

An Intent can be converted to a URI using the method

    public String toUri(int flags)

The resulting URI is basically the data URI of the Intent with a new fragment suffix.

For example,


    new Intent(
        "xper.action.TO_URI", 
        new Uri.Builder().
            scheme(
                "http").
            authority(
                "justanapplication.wordpress.com").
        build()).
    toUri(0))

returns the string

    https://justanapplication.wordpress.com#Intent;action=xper.action.TO_URI;end

There is only one non-zero value which can be passed as the flags argument. It is defined by the class constant


    URI_INTENT_SCHEME

Using it causes the resulting URI to have the intent scheme.

For example,


    new Intent(
        "xper.action.TO_URI", 
        new Uri.Builder().
            scheme(
                "http").
            authority(
                "justanapplication.wordpress.com").
        build()).
    toUri(Intent.URI_INTENT_SCHEME))

returns the string

    intent://justanapplication.wordpress.com#Intent;scheme=http;action=xper.action.TO_URI;end

If the Intent does not contain a data URI then only the fragment is returned, although it is arguable whether a fragment on its own is actually a legal URI.

The fragment will specify everything present in the Intent although in the case of extras only primitive types and Strings will be included.

5.2 Constructing An Intent From a URI

An Intent can be constructed from a URI by using the method

    public static Intent parseUri(String uri, int flags)

Like the toUri(int) method the only legal non-zero value for the flags argument is the value of URI_INTENT_SCHEME.

If the URI does not end with a fragment in the format generated by the toUri(int) method then it will be used as the data URI of the resulting Intent.

The round trip from Intent to URI string to Intent is not guaranteed to be exact. Given an Intent i, then

    i.filterEquals(Intent.parseUri(i.toUri(0), 0))

may return either true or false depending upon the contents of i.

6. Class Constants

6.1 Pre-defined Actions

A large number of actions are predefined by the system using class constants of the form

    ACTION_<name>

The documentation for each definition specifies its semantics when used by the system.

Applications can also define their own actions as they see fit. There are numerous examples through out the Android source code.

6.2 Pre-defined Categories

A number of categories are predefined by the system using class constants of the form

    CATEGORY_<name>

Applications can also define their own categories, but they cannot be used to affect the behaviour
of the system in the same way as some of the pre-defined categories do.

6.3 Pre-defined Extra Keys

A number of extra keys are predefined by the system using class constants of the form

    EXTRA_<name>

Applications can re-use these if they choose or define their own as necessary. As with actions there are numerous examples through out the Android source code.

6.4 Flags

The values which can be bitwise-or-ed together and passed to the setFlags(int) method are defined
by class constants of the form

    FLAG_<name>

The set of possible values is effectively fixed by the system. Depending upon the underlying implementation it might be possible for an Application to use some of the currently unused values for its own purposes, but this is obviously not a good idea, and also unnecessary given the ability to specify arbitrary data via extras.

7. The Source Bounds Mystery

In addition to the parts described above an Intent may contain one additional thing, an instance of android.graphics.Rect.

There is a method to get it

    public Rect getSourceBounds()

and a method to set it

    public void setSourceBounds(Rect r)

There is even a flag

    FILL_IN_SOURCE_BOUNDS

that can be used in conjunction with the fillIn(Intent,int) method.

It is copied by the copy constructor and represented in the URI string created from an Intent but what exactly is it for ?

According to the method documentation it specifies

the bounds of the sender of this Intent, in screen coordinates

It is not a historical artefact since it was added to Android 2.1, API Level 7 so its only been around for about the last year.

Given that extras can be used to pass almost anything at all and most definitely a Rect, why add a dedicated field for something which has such limited applicability ?

A brute force search of the Android source code turns up a handful of places where it is being used. In one place it looks as though it is being used as a replacement for using an extra, but on the face of it there does not seem to be a compelling reason why, especially given that other data is still being transferred as extras.

To possibly misquote someone or other it looks not unlike a hack.


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

Advertisements

12 Comments »

  1. […] Intent Filter may specify zero or more actions to match against the action specified by an […]

    Pingback by The Android Intent Based APIs: Part Three – Intent Filters « Just An Application — December 16, 2010 @ 9:37 pm

  2. […] values which can be used as Intent flags are defined by the System as Intent class constants. The majority of them, those with the prefix […]

    Pingback by The Android Intent Based APIs: Part Four – Activities And Intents « Just An Application — February 3, 2011 @ 2:56 am

  3. […] an Intent explicitly specifies a component then the Intent resolves to that Application Component irrespective of whether it is actually an […]

    Pingback by The Android Intent Based APIs: Part Four – Activities And Intents « Just An Application — February 3, 2011 @ 2:56 am

  4. […] the Intent specifies a package then the search is confined to the Activities in that Application […]

    Pingback by The Android Intent Based APIs: Part Four – Activities And Intents « Just An Application — February 3, 2011 @ 2:57 am

  5. […] an Intent explicitly specifies a component then the Intent resolves to that Component even if it is not a Service or does not […]

    Pingback by The Android Intent Based APIs: Part Five – Services And Intents « Just An Application — February 16, 2011 @ 7:47 pm

  6. […] the Intent specifies a package then the search is confined to the Services in that Application […]

    Pingback by The Android Intent Based APIs: Part Five – Services And Intents « Just An Application — February 16, 2011 @ 7:47 pm

  7. […] an Intent explicitly specifies a component then the Intent resolves to that Component if it is a BroadcastReceiver. If the specified Component […]

    Pingback by The Android Intent Based APIs: Part Six – Broadcast Intents « Just An Application — February 17, 2011 @ 2:52 pm

  8. […] also used to pass any of the flags defined by the Intent class constants which can be passed to the Intent.fillIn() […]

    Pingback by The Android Intent Based APIs: Part Seven – IntentSenders And PendingIntents « Just An Application — February 21, 2011 @ 3:58 pm

  9. […] the connection represented by the ConnectionRecord was the only one using the Intent/IBinder binding then it will also remove that […]

    Pingback by Adventures In Distributed Garbage Collection: Part Twelve – Unbinding From A Service « Just An Application — May 23, 2012 @ 6:01 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: