Before I dive into the launch mode of an activity, we need
to first understand the term, 'Task'. A task is the stack ("Last in,
First out") which contains a collection of activity instances. Normally,
when a user starts an app a new task will be created, and the first
activity instance is called as a root of the task. The Android system
can hold multiple tasks at the same time and only one task is in the
foreground. Similar to a double click of the home key on iOS, if you
long press the HOME key on Android you'll be presented with a list of
your currently running applications. You can select any one of these
applications (which are currently running in the background) in order to
bring it to the foreground and interact with the app! Of course,
because background tasks do tend to use up your processor cycles you
should try to keep your backgrounded apps to a minimum to ensure your
phone performs optimally.
Now let's look at the launch mode of an activity. Launch mode allows
you to define how a new instance or the existing instance of an activity
is associated with the current task. The activity launch mode has four
valid values:
1
|
<activity android:launchMode = ["standard" | "singleTop" | "singleTask" | "singleInstance"] ../>
|
The 'standard' is the default value. The four values fall into two groups:
- 'standard' and 'singleTop' can instantiate multiple activity instances and the instance will stay in the same task.
- For 'singleTask' or 'singleInstance', the activity class uses the
singleton pattern, and that instance will be the root activity of a new
task. Let's examine each value:
"standard":
Multiple instances of the activity class can be instantiated and
multiple instances can be added to the same task or different tasks.
This is the common mode for most of the activities.
"singleTop":
The difference from 'standard' is, if an instance of activity already
exists at the top of the current task and system routes intent to this
activity, no new instance will be created because it will fire off an
onNewIntent() method instead of creating a new object. Let's take the
Twitter-oauth integration as example. Suppose we have the following
NewsDetailActivity declared:
|
<activity android:name=".NewsDetailActivity" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:scheme="oauth" android:host="twitt"></data>
</intent-filter>
</activity>
|
And in the activity we will have the following code to start the
oAuth process which will bring up the browser to visit Twitter's
authorization page:
So if a user goes to this activity in the current task (let's say
Task #1), a new instance of NewsDetailActivity will be pushed to the
top. Now start the oAuth process and the Android system routes the
intent to the default Android Browser application, whose launch mode is
'singleTask'; that means a new Task begins for the browser activity,
let's say Task #2. Hence the 'NewDetailActivity' is still on top of the
Task #1.
After the user finishes the authorization on Twitter's page, the
customized callback url 'oauth://twitter' will be invoked. Because
NewsDetailActivity declares support for that data type, the system
routes the intent to the 'NewsDetailActivity'. Since
NewsDetailActivity's launch mode is 'singleTop', and the top instance on
Task #1 will be reused, its onNewIntent() method will be invoked to
continue oAuth's last step. Here is the code for that:
|
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Uri uri = intent.getData();
if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {
String verifier = uri.getQueryParameter("oauth_verifier");
try {
//load other persisted data goes here
....
//get the request-token from prefs and request the access token
TwitterFactory twitter = new TwitterFactory().getInstance();
requestToken = prefsManager.getRequestToken();
prefsManager.clearTwitterRequestToken();
twitter4j.auth.AccessToken accessToken = twitter.getOAuthAccessToken(requestToken, verifier);
// save the access token
prefsManager.saveTwitterOauth(accessToken.getToken(), accessToken.getTokenSecret(), accessToken
.getScreenName());
//other logics to do the post to twitter
....
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
}
}
|
"singleTask":
A new task will always be created and a new instance will be pushed to
the task as the root one. However, if any activity instance exists in
any tasks, the system routes the intent to that activity instance
through the onNewIntent() method call. In this mode, activity instances
can be pushed to the same task. And if the user clicks the BACK key
from the singleTask activity, the system will return the user to the
previous activity.
This mode is useful for activities that act as the entry points. For
example, in a tab-based Twitter client, we can declare the main
TabActivity as singleTask. This is also useful if you only want a single
instance of the activity to be created. One example of singleTask is
the Browser application. Let's take a Twitter-client-application as
example, where the main activity is a TabActivity and has three tabs:
tweets, replies and messages. And we want to suppose auto-refresh and
notification function when app is running.
In AndroidManifest.xml, we have:
1
2
|
<activity android:name=".MainTabActivity" android:launchMode="singleTask" android:alwaysRetainTaskState="true" android:windowSoftInputMode="adjustPan">
</activity>
|
Suppose we have the notification for new tweet/replies/messages, and we
hope that clickong on the notification will take the user to the current
TabActivity, and switch to corresponding tabs. (Here we suppose the app
is not closed before user clicks the notification). Because the
MainTabActivity's launch mode is singleTask, system routes the intent
to the MainTabActivity by calling its onNewIntent() method. And in this
method, we can determine which tab to switch to according to the bundle
data in the intent.
"singleInstance":
Same as 'singleTask', except that the no activities instance can be
pushed into the same task of the singleInstance's. That means, the
activity with launch mode is always in a single activity instance task.
This is a very specialized mode and should only be used in the
applications that are implemented entirely as one activity.
Summary:
Understanding the launch mode will help you design better navigation and
implement some special cases. Here we only discuss the launch mode from
the AndroidManifest file. Android launch mode can also be declared
using the Intent flags, such as FLAG_ACTIVITY_NEW_TASK,
FLAG_ACTIVITY_CLEAR_TOP and FLAG_ACTIVITY_SINGLE_TOP. And we will create
more posts to talking about intent flags in the near future, so stay
tuned!
0 comments:
Post a Comment