2013年2月5日星期二

Android Activities and Tasks series – Activity Attributes



The previous post of the Android Activities and Tasks series explained the concept of Android’s intents. We have seen how to use them to launch activities and how to utilize intent flags to customize the behavior of the launch to our needs.
In this post, we focus on activities themselves and explain the properties we can set on an activity or task to influence the activity launch behavior on the receiver side. In detail:
  1. activity launch modes
  2. task attributes
  3. task affinities of activities

Introduction

In Android, every activity must be defined in the project’s AndroidManifest.xml file, using activity tags placed as children of the application tag. Here, a set of attributes can be specified for each activity. The only mandatory attribute is android:name, which defines the fully qualified name of the activity’s implementation class. This class must be a subclass of Activity. To avoid full qualification of the name, you can use the special “.” prefix, which will be interpreted by Android as the package name of the activity’s application. An example:
1<activity android:name=".MyActivity" />

Launch Modes

The intent flags (defined on the calling intent) in conjunction with the launch mode (specified on the called activity) define the actual outcome of the launch. There are four different launch modes: standardsingleTopsingleTask and singleInstance. The first two only have an impact on the activity, while the last two have important consequences for the task the activity should run in.

standard

A new instance of the activity is created and pushed on top of the current task. The created instance will receive the intent. This is the default and most commonly used behavior.

singleTop

Launch Mode "singleTop"
Behaves exactly like standard, with one important exception: If an instance of the same activity is already on top of the task stack, then this instance will be reused to respond to the intent. No new activity instance is created. Note that, if such an instance is present in the stack, but not at the top, the behavior is identical to launch mode standard and will thus result in a new instance being created.
Although the callback onResume(..) is invoked on that activity instance, take note thatgetIntent() will still return the original intent, not the new intent! Thus, to receive the new intent, a singleTop activity must implement the method onNewIntent(Intent). This method is invoked by Android upon launch with the new intent object as parameter.
Using singleTop within an application is useful to avoid starting the same activity multiple times in a row. It will not enforce that you have only one instance of a certain activity type in one task stack, because an activity instance is only reused if it is at the top of the stack.

singleTask

Launch Mode "singleTask"
The activity is always started in a new task. Thus, a singleTask activity will always be the root activity of a task. Activities launched from this activity will be put on top of the new task’s stack by default.
Moreover, a singleTask activity acts as a singleton, i.e. it is impossible to have a second instance of such an activity, neither in this task nor in any other task. Upon launching the activity a second time, the intent is delivered to the existing instance via onNewIntent(Intent).
This behavior is useful for activities that act as entry points to your application. With this, you can enforce that these always start in a new task. At the same time, you ensure that the activity is never instantiated a second time and intents are delivered to the already existing activity.

singleInstance

Launch Mode "singleInstance"
Behaves exactly like singleTask but never allows other activities to become part of the new task. The activity will remain the sole activity instance in the task. As soon as another activity is started from here, the new activity will be automatically assigned to a different task, just like the intent would have FLAG_ACTIVITY_NEW_TASK set.
Use cases of this mode are rare. Any activity started from your singleInstance activity will be placed in another task. Thus, when the user returns to your singleInstance task, activities from the new task are not resumed and presented to the user, which is not desirable in the majority of cases.
It is useful for portal-like apps that only start activities from other applications, making it unnecessary to specify FLAG_ACTIVITY_NEW_TASK for all your intents and enabling the user to resume your portal app from the home screen without resuming activities that were started from there (as these will be put in other tasks automatically).

Task Attributes

Besides for activities, Android allows you to set properties for tasks in order to customize the way the task should behave. These particular properties are therefore associated with a task and not an activity. But, as there is no direct representation of a task in the manifest XML structure, task attributes are defined as an attribute of the activity that is used as the root of this task. Thus, such a task property is only effective if it is defined on a task’s root activity; it is ignored in all other cases.
Android supports the following task attributes:

alwaysRetainTaskState

Normally, a task may be cleared by Android, meaning all activities besides the root activity are destroyed and removed from the task’s stack. After the clear has taken place, the user will see the task’s root activity when bringing the task to the foreground.
If a task has not been used in a while, Android automatically clears it. The assumption here is that, given that the task remained in the background for some time, the user is no longer interested in the activities she had navigated to. Instead, she wants to start at the root activity when bringing the task to the foreground.
The attribute alwaysRetainTaskState is a boolean value to prevent Android from clearing a task. If set to true, the activities on top of the root activity are always kept. This can be useful for tasks that carry a lot of state, like the tabs of a web browser, where you assume that the user wants to retain the state. Be aware that, as Android can no longer clear the task, the system may decide to kill your app entirely in order to reduce memory consumption.
The default is false.

clearTaskOnLaunch

When this boolean attribute is set to true, a task will be cleared everytime it is relaunchedfrom the home screen. All activities on top of the root activity are removed. This means that, on relaunch, the task state is always discarded and the root activity is displayed to the user.
Thus, upon relaunch and when using this mode, activities on top of the root activity are usually finished. However, note that there is an important exception: If a to-be-removed activity allows reparenting (via attribute allowTaskReparenting) and this activity has anaffinity for a different existing task, the activity will be moved to that task instead of being finished. We will address the topic of task reparenting and activity affinities in a minute.

Other task-related attributes

The following attributes are task-related, but define behavior for activities. As such, you can set them on any activity, not just on root activities.

finishOnTaskLaunch

If this boolean attribute is set to true, the activity is finished when the task is relaunched from the home screen. With this, you can mark an activity as irrelevant for the task state, meaning that we do not need to wait for Android to perform a task clean in order to forget this particular activity instance. Note, however, that the activity is not finished when bringing the task to the foreground using the list of recently started activities (by pressing and holding the home key).

noHistory

If this boolean attribute is set to true, the activity instance never remains in the task stack. As soon as the user navigates away from it, the activity instance is immediately finished, and the user cannot return to it.
This does not only mean that the activity is no longer in the task stack upon returning (i.e. relaunching the app or resuming via the list of recently started activities). The user will also never be able to return to it via the back key.

Task Affinities

Every activity has a so-called affinity for a certain task. You can think of an affinity as thename of a task this activity shall run in. By default, all activities within the same application are affine to the same task.
The task affinity of an activity has two consequences:

(1)

When launching the activity with the intent flag FLAG_ACTIVITY_NEW_TASK, Android looks for an already running task fitting this affinity, more precisely, a task whose root activity has the same affinity. If such a task exists, it is brought to the front, and the launched activity is pushed on top of its stack. If such a task does not exist, a new one is created, and activities launched at a later time will be put into this task, given that FLAG_ACTIVITY_NEW_TASKis used and the activity’s affinity is the same.
Example
Task Affinities: Launch new activities into an existing task
Suppose you are listening to a song within a music player application while have a running web browser application in the background. Now, you want to visit the website of the artist of the music track you are listening to. Let’s suppose that the music player fires an intent withFLAG_ACTIVITY_NEW_TASK to view the artist’s website inside a browser activity.
In our example, the browser application is already running in the background. As the browser activity belongs to the browser application and, as such, has an default affinity for the task the browser was launched into, the affinity will imply that the artist’s website will be displayed in a new activity that is pushed on the stack of the browser task, not the music player task.

(2)

If the activity’s property allowTaskReparenting is set to true, the activity can move from one task to another, namely from the one it was started in to the one it has an affinity for. This happens if this particular task is brought to the foreground, and an instance of our activity is present in another task at that time. Then, this activity is moved to the task that is coming to the front, and pushed on top of its stack.
Example
Task Affinities: Reparenting
Suppose you have a browsing activity that allows task reparenting and is capable of showing the contents of a web page. The activity is part of the browser application. Now, this activity is started from another application, your music player, to show the website of your favorite artist. Assuming the flag FLAG_ACTIVITY_NEW_TASK is not set, this will imply that the activity becomes part of the music player’s task.
If we now open the browser application, the activity (which has task reparenting enabled) will move from the music player task into the browser task. Why is that? Remember that the activities within an application share an affinity for the same task, which is why the browser activity is affine to the already existing browser task. Thus, upon opening the browser application, we are presented with the artist’s webpage we viewed earlier from within the music player application, and the browser activity is now on top of the browser task’s stack.
Android defines the following affinity-related attributes for activities:

android:taskAffinity

An arbitrary string to define the task affinity; more specifically, the name of the task the activity preferrably runs in. When launched with intent flag FLAG_ACTIVITY_NEW_TASK, the activity is then put into this particular task, if present. Also, this value is used to determine the target activity for task reparenting.
The default value is the package name of the application this activity is defined in, which is why activities of the same application share an affinity for the same task by default.

android:allowTaskReparenting

A boolean value to allow or prohibit task reparenting. If set to true, running activities may be moved to the task they have an affinity for once this task is brought to the foreground.

Conclusion

In this post, we have seen that not only the caller of an activity can alter the launch behavior of an activity. Customizations of that behavior can also be defined on the receiver’s end, by setting a particular launch mode on an activity. In addition, we may influence the behavior of tasks by defining task attributes on them. Moreover, through usage of activity affinities, we can group related activities to allow Android to put them into the same task, if possible.
The next blog post will conclude this series with possible use cases and practical examples.

Android Activities and Tasks series – Intent flags



The previous post of the Activities and Tasks series gave an introduction to the concepts behind activities and tasks. We have seen that activities correspond to visible screens in the UI, that activities are grouped as stacks within tasks, and tasks are sent to the background and foreground as atomic units. In this post of the series, we focus on Android’s intent concept and address the following questions:
  1. What are intents?
  2. How do we use them to launch activities?
  3. What options (flags) does Android provide to customize this launch? (e.g. in terms of target task or activity instance creation)

In order to ease understanding of intents and their various flags, akquinet Modular and Mobile Solutions provides an Android application to experiment with intent flags. With it, you can start different types of activities, specifying the intent flags you want to pass, and examine the effects. Install it by pointing your Android browser to this URL.

Intents and activity launching

An intent is a data structure containing a description of a to-be-performed operation. This operation primarily consists of an action and of data to operate on, although there are various optional parameters to describe the operation in more detail. Technically speaking, this action is just a constant, either a pre-defined one from Android, like ACTION_VIEW orACTION_EDIT, or a custom one that we can define ourselves.
An intent is always handled by an Android component, namely an activity, a service or a broadcast receiver. In this blog post series, we will focus on activities as intent handlers. For our purposes, an intent is used to launch an activity.
To create an intent and launch an activity with it, we can write the following code within ourcaller activity:
1Intent intent = new Intent(Intent.ACTION_VIEW,
2    Uri.parse("http://www.akquinet.com/")
3startActivity(intent);
In this example, we give two parameters to the Intent constructor: The action to be performed (here view) and the corresponding data (here a web page URL). Then, we simply pass the Intent object to startActivity(Intent). Basically, what we do here is telling Android that we want to view the akquinet homepage. Android will look for activities capable of handling that intent, which in this case is a web browser activity, and start it. Note that any activity can be declared capable of handling certain intent types by defining intent filters in the application’s AndroidManifest.xml file.
Instead of letting Android choose a suitable activity, an intent may also name the to-be-launched target activity explicitly by specifying the target activity’s class, like so:
1Intent intent = new Intent(this, MyActivity.class);
2startActivity(intent);
When run, this code snippet has the following consequences:
  1. A new instance of MyActivity is created
  2. The instance is pushed on top of the current task’s stack, which is the one the calling activity is in.
  3. The activity is started and brought to the foreground.

Customizing the launch of activities via intent flags

The described behavior can be modified by specifying intent flags on the intent instance before passing the intent to startActivity(Intent), for example:
1intent.addFlag(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
This will bring an existing instance of the called activity type present in the current stack to the foreground (if there is such an instance) instead of creating a new instance. Android 2.0 defines various intent flags, which are now explained in more detail.
FLAG_ACTIVITY_BROUGHT_TO_FRONT
This flag is set by the system as an indication that an existing activity with launch modesingleTask has been called and reused. Activity launch modes are discussed in more detail in the next blog post.
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
Bringing a task to foreground after using flag CLEAR_WHEN_TASK_RESET
This flag marks the called activity and all activities put on top of it to be cleared (removed from the stack) when the task is reset. This reset is performed as soon as the task is brought to the foreground using FLAG_ACTIVITY_RESET_TASK_IF_NEEDED, which is always set by the system when the user resumes a task from the home screen, the main menu or the list of recently started activites.
You can use this to define a point in the task stack from where all started activities should be “forgotten” once the task is sent to the background.
FLAG_ACTIVITY_CLEAR_TOP
Using flag CLEAR_TOP to launch an activity
If there is already an instance of the called activity type present in the stack, then this instance is brought to the foreground instead of creating a new instance. Also, all activities in the stack that reside on top of that instance are cleared from the stack. For example, assuming that the current activity stack is ABCDE, launching an activity of type C will clear activities D and Efrom the task and result in the stack ABC.
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
Normally, when launching an activity in a new task, the target activity is shown in the list of recently launched activities (accessible via long press of the home button). For example, this is the case when launching the browser from your activity to render a web page. Using this flag, we can suppress that behavior, effectively excluding the target activity from the list of recently launched activities.
FLAG_ACTIVITY_FORWARD_RESULT
By default, in order for an activity B to be able to propagate a result back to the calling activityAA has to use the method startActivityForResult(Intent) instead of methodstartActivity(Intent) to start B. When B defines its result via setResult(int resultCode) and finishes, the callback method onActivityResult() is called on Awhere the result can be retrieved.
Resulting forwarding with Flag FORWARD_RESULT
Suppose now that B calls another activity C, which defines the result, and we want to forward that result back to A. In order to avoid having to pass results manually from B to A, Android provides this flag.
In our example, this means that B calls C, specifying the forward flag and usingstartActivity(Intent)C may then define a result, which is forwarded back to Aautomatically once C and B are finished. The callback method onActivityResult() is called on A but not called on the intermediate activity B as B only forwards the result instead of explicitly handling it.
Note that the flag may only be used in conjunction with startActivity(Intent), not with startActivityForResult(Intent), because an activity using the latter method marks the end of a forwarding chain and should handle the result in its callback method.
FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
This flag is set by the system as an indication that an activity has been launched from the list of recently launched activities.
FLAG_ACTIVITY_MULTIPLE_TASK
This flag has no effect unless used in conjunction with FLAG_ACTIVITY_NEW_TASK. If used together, a new task is always created and a new instance of the called activity type becomes the first (and at that time, the only) element of that task.
FLAG_ACTIVITY_NEW_TASK
If this flag is used on its own (without FLAG_ACTIVITY_MULTIPLE_TASK), a new task is only created if there is not already a task present that the target activity has an affinity for (task affinities are examined more closely in the next blog post). If such a task already exists, the activity is put into that task instead of creating a new one.
As all activities specified within the same application have a default affinity to remain in the same task (this can be customized), starting an activity defined in the same application as the calling activity using this flag will not create a new task, unlessFLAG_ACTIVITY_MULTIPLE_TASK is specified as well.
FLAG_ACTIVITY_NO_ANIMATION
Introduced with Android 2.0, this flag disables the usual animation accompanied by an activity transition. For example, this can be used to call an activity that itself launches another activity without further user interaction, resulting in only one transition animation to be shown instead of two.
FLAG_ACTIVITY_NO_HISTORY
This flag implies that the called activity is not kept in the task’s stack. When navigating away, the user cannot return to it. For example, this is useful to launch activities that perform an immediate dispatch to another activity. This activity is consequently skipped if the user presses the back button as it is not kept in the stack.
FLAG_ACTIVITY_NO_USER_ACTION
This flag may be used as an indication that the activity transition is not an action directly performed by the user but rather an automatic one. For example, this is true for the call-in activity that is automatically launched when the device receives a phone call. Technically, it means that the invocation of callback method onUserLeaveHint() on the caller activity is suppressed.
FLAG_ACTIVITY_REORDER_TO_FRONT
Starting an activity with Flag REORDER_TO_FRONT
If there is already an instance of the called activity type present in the task stack, that instance is brought to the foreground while the rest of the stack is kept intact. This effectively reorders the stack. For example, assuming that the current activity stack is ABCDEF, launching an activity of type C using this flag will result in the new stack ABDEFC.
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
This flag has no effect unless the intent creates a new task or brings an existing task to the foreground. In that case, the task is reset, meaning that task affinities are applied (resulting in activities being moved from or to this task) and that, given thatFLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET is set as well, the task is cleared according that flag’s specification.
FLAG_ACTIVITY_SINGLE_TOP
If an instance of the target activity is already at the top of the stack, no new instance is created.

Conclusion

The akquinet Mobile and Modular Solutions provides an Android application to help you understand the effects of the intent flags. It lets you start exemplary activities using arbitrary intent flags and inspect the effects on the resulting task stack.
Intent Flags Tool - Main screen
Intent Flags Tool - Main screen
Intent Flags Tool - Selecting Flags
Intent Flags Tool - Selecting Flags
You can download the tool here. The source code is also available on github.
All described behavior not only depends on intent flags specified, but also on attributes defined on the called activity type (in AndroidManifest.xml), including task affinities and launch modes. While intent flags are specified by the caller, launch modes and affinities are properties of the callee. Those will be discussed in the next post of this series.

Android Activities and Tasks series – An introduction to Android’s UI component model



With Android, Google implemented its very own UI component model, introducing various new terms and concepts probably unfamiliar to most developers. Although well designed and powerful, it is unconventional, has a high learning curve and, in some respects, lacks sufficient documentation. Basic use cases may be quick to implement, but in order to develop more complex applications, a thorough understanding of Android’s UI component model is essential.
This series aims at providing this understanding while focusing on the UI related concepts of activities and tasks.

Overview

This series is composed out of four posts:
  1. This first post gives an introduction to Android’s UI component model and briefly explains the concepts behind activities and tasks.
  2. The second post details the various intent flags used to customize the start of activies and influence corresponding behavior (caller).
  3. The third post deals with the different activity properties specifyable for the called activity (callee).
  4. The fourth post concludes the series with an exploration of the different combinations of intent flags and activity properties.

Introduction to Android’s component model

In Android, the visible UI of an application is composed out of so-called activities. An activity usually corresponds to one screen the user interacts with, responsible for doing one particular job within an application, like showing a list of files or rendering a web page. Activities display UI controls (so-called views) to the user and react to events. They are each implemented within a single class that extends Android’s Activity base class.
Activity to view and update alarms
Most applications consist of multiple activities, allowing the user to navigate from one activity to another. Technically, this usually implies creating a new activity instance and starting it. From a developer’s perspective, this is achieved by using so-called intents (that either specify the to-be-called activity explicitely or describe the “intention” accurately enough for Android to choose a suitable one).
A distinct characteristic of Android is the possibility to start activities provided by other applications, enabling the user to seamlessly navigate between applications without necessarily being aware of this fact, forming a cohesive user experience. At any point, the user may press the “back” button of his device to return to the previously viewed activity.
To provide this functionality, Android keeps stacks of activities the user has opened. Such stacks are referred to as tasks. In many cases, a new task is started by launching an application from the application menu or home screen. The called “bootstrap” activity instance of the launched application becomes the first one in the task’s stack. When navigating from this activity to another one, the new one is pushed on the stack, which is then holding two activity instances.
A task holding four activities
Thus, a task is what the user experiences as “application”, while activities contained in a task may actually come from different applications. For example, an activity might display a list of locations that each, when clicked, open a Google Maps activity to show the location on a map. This map activity is not part of the original application but is experienced by the user as such. Android pushes it on top of stack of the current task. Pressing the “back” button of the device at any time will remove the (currently visible) top activity instance from the task’s stack, close it and return to the underlying activity, thus providing an activity history not unlike the one known from web browsers.
It is possible to have multiple tasks present in the system at the same time. The user can bring a particular task to the foreground, work with it, then switch back to a task started earlier, or create a new task by launching another application. The stack of activities for each task is retained.
From the user’s perspective, such a task switch is performed by leaving the current task via pressing the “home” button and launching another application, which will either create a new task or bring an existing one to the foreground, depending on if the application was already started before. In addition, pressing and holding the “home” button will display a list of recent tasks to switch to directly, similar to ALT+TAB known from Desktop operating systems.
Switching tasks by by long press of home button
When speaking of activities in a task’s stack, we always mean activity instances. It is possible to have multiple instances of the same activity within the same task, e.g. an activity that shows the content of a certain folder within a file explorer application. Clicking on a folder opens the same activity with a different folder. Also, two instances of the same activity may run in two different tasks, e.g. an activity to view a location on a map.
The described behavior for activities and tasks can be customized by specifying certain intent flags when starting an activity. For example, we may want to bring an activity already existing in the task’s stack to the top instead of creating a new instance of that activity. These possibilities are explored in detail in the next post of this series.