Fragment trong Android

Posted: 28/11/2014 in Android

S/t : http://laptrinhtuduy.wordpress.com/2014/04/29/fragment-trong-android/

Fragment là một phần giao diện người dùng hoặc hành vi của một ứng dụng. Fragment có thể được đặt trong Activity, nó có thể cho phép thiết kế activity với nhiều mô-đun. Có thể nói Fragment là một loại sub-Activity.

  • Fragment cũng có layout của riêng của nó, cũng có các hành vi và vòng đời riêng.
  • Chúng ta có thể thêm hoặc xóa Fragment trong một Activity trong khi Activity này đang chạy.
  • Có thể kết hợp nhiều Fragment trong một Activity để xây dựng giao diện người dùng đa khung.
  • Một Fragment có thể được sử dụng trong nhiều Activities.
  • Vòng đời của Fragment có quan hệ chặt chẽ với vòng đời của Activity đang dùng nó điều này có nghĩa là khi Activity bị tạm dừng thì các Fragment sẽ dừng lại.
  • Fragment có thể thực hiện một hành vi mà không có trong thành phần giao diện người dùng.
  • Fragment được thêm vào API 11 trở lên.
  • Bạn có thể tạo các Fragments bằng cách kế thừa lớp FragmentFragment được thêm vào layout bởi thẻ <fragment>

Trước khi giới thiệu về Fragment, thì có một hạn chế là tại cùng một thời điểm chúng ta chỉ có thể hiển thị một Activity duy nhất trên màn hình. Vì thế chúng ta không thể chia màn hình thiết bị ra thành nhiều phần và kiểm soát các thành phần khác nhau này một cách riêng biệt. Nhưng với Fragment thì màn hình được linh hoạt hơn, xóa bỏ việc chỉ có duy nhất 1 Activity trên màn hình tại một thời điểm. Bây giờ chúng ta cũng chỉ có một Activity duy nhất nhưng Activity này có thể chứa nhiều Fragment vớilayout, sự kiện, và vòng đời. Dưới đây là một ví dụ cụ thể về sử dụng Fragment. Thiết bị máy tính bảng với màn hình lớn thì một Activity có thể chứ 2 Fragment, còn thiết bị cầm tay với màn hình nhỏ thì có thể có 2 Activities với mỗi Activity là một Fragment.Vòng đời của một Fragment Mỗi Fragment có vòng đời riêng của nó, và vòng đời này giống với vòng đời của một Activity. Giai đoạn 1: Khi một Fragment được tạo, nó sẽ đi qua một số trạng thái:

  1. onAttach()
  2. onCreate()
  3. onCreateView()
  4. onActivityCreated()

Giai đoạn 2: Khi một Fragment được hiển thị:

  1. onStart()
  2. onResume()

Giai đoạn 3: Khi Fragment chạy ẩn dưới nền:

  1. onPause()
  2. onStop()

Giai đoạn 4: Khi hủy một Fragment:

  1. onPause()
  2. onStop()
  3. onDestroyView()
  4. onDestroy()
  5. onDetach()

Cách tạo một Fragment Sau đây là một số bước để tạo một Fragment đơn giản:

  1. Đầu tiên bạn phải nghĩ xem cần bao nhiêu Fragment trong một Activity. VD: Bạn muốn 2 Fragment cho trường hợp màn hình nằm ngang, và 1Fragment trong trường hợp màn hình đứng.
  2. Tiếp theo, dựa trên số Fragment đã tính, tạo các lớp kế thừa lớp Fragment. Lớp Fragmentcó nhiều phương thức callback, bạn có thể override bất kỳ phương thức nào dựa trên yêu cầu.
  3. Tương ứng với mỗi Fragment, bạn cần phải tạo các file Layout (XML file).
  4. Cuối cùng, chỉnh sửa file Activity, để xác định vị trí của các Fragment theo yêu cầu.

Đây là một số phương thức quan trọng bạn có thể Override trong lớp Fragment của bạn.

  • onCreate(): Hệ thống gọi phương thức này khi tạo Fragment. Bạn nên khởi tạo các thành phần thiết yếu của Fragment  mà bạn muốn giữ lại khi Fragment được tạm dừng, dừng lại hoặc tiếp tục.
  • onCreateView(): Hệ thống gọi phương thức này khi Fragment vẽ giao diện của nó lần đầu tiên. Để vẽ giao diện cho Fragment bạn cần phải return View từ phương thức này. Bạn có thể return null nếu Fragment không cung cấp giao diện.
  • onPause(): Hệ thống gọi phương thức này như là để đánh dấu lần đầu người dùng rờiFragment. Đây là nơi bạn ghi lại bất kỳ thay đổi nào và thay đổi cần tiếp tục tồn tại ngoài lần dùng này.

Ví dụ: Trong ví dụ này sẽ hướng dẫn bạn cách tạo và sử dụng Fragments. Chúng ta sẽ tạo 2Fragments, một trong số chúng được dùng khi màn hình điện thoại nằm ngang và một cái còn lại được dùng khi màn hình đứng. Tạo project với tên: FragmentExample File: res/values/strings.xml

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">FragmentExample</string>
    <string name="action_settings">Settings</string>
    <string name="hello_world">Hello world!</string>
    <string name="landscape_message">This is Landscape mode fragment</string>
    <string name="portrait_message">This is Portrait mode fragment</string>
</resources>

File: res/layout/lm_fragment.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="#7bae16">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/landscape_message"
        android:textColor="#000000"
        android:textSize="20px"/>
</LinearLayout>

File: res/layout/pm_fragment.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal"
    android:background="#666666">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/portrait_message"
        android:textColor="#000000"
        android:textSize="20px"/>
</LinearLayout>

File: res/layout/activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment
        android:name="com.laptrinhtuduy.fragments"
        android:id="@+id/lm_fragment"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:layout_width="0dp"/>
    <fragment
        android:name="com.laptrinhtuduy.fragments"
        android:id="@+id/pm_fragment"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:layout_weight="2"/>
</LinearLayout>

File: src/com.laptrinhtuduy.fragments/LM_Fragment.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.laptrinhtuduy.fragments;
import com.laptrinhtuduy.fragmentexample.R;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class LM_Fragment extends Fragment{
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.lm_fragment, container, false);
    }
}

File: src/com.laptrinhtuduy.fragments/PM_Fragment.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.laptrinhtuduy.fragments;
import com.laptrinhtuduy.fragmentexample.R;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class PM_Fragment extends Fragment{
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.pm_fragment, container, false);
    }
}

File: src/com.laptrinhtuduy.fragmentexample/MainActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.laptrinhtuduy.fragmentexample;
import com.laptrinhtuduy.fragments.LM_Fragment;
import com.laptrinhtuduy.fragments.PM_Fragment;
import android.os.Bundle;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.res.Configuration;
import android.view.Menu;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Configuration config = getResources().getConfiguration();
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        if(config.orientation == Configuration.ORIENTATION_LANDSCAPE){
            LM_Fragment lm_Fragment = new LM_Fragment();
            fragmentTransaction.replace(android.R.id.content, lm_Fragment);
        }else{
            PM_Fragment pm_Fragment = new PM_Fragment();
            fragmentTransaction.replace(android.R.id.content, pm_Fragment);
        }
        fragmentTransaction.commit();
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
}

Kết quả:

Khi để đứng màn hình:

Sau đó: ctrl + F11 => Màn hình nằm ngang:

DOWNLOAD: SOURCE CODE

Activity Attributes

Posted: 29/04/2013 in Android
Thẻ:

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:

<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: standard, singleTop, singleTask 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 that getIntent() 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 relaunched from 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 an affinity 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 the name 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_TASK is 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 with FLAG_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.

Intent flags

Posted: 29/04/2013 in Android
Thẻ:

http://blog.akquinet.de/2010/04/15/android-activites-and-tasks-series-intent-flags/

 

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 or ACTION_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 our caller activity:

1
2
3
Intent intent = new Intent(Intent.ACTION_VIEW,
    Uri.parse("http://www.akquinet.com/")
startActivity(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:

1
2
Intent intent = new Intent(this, MyActivity.class);
startActivity(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:

1
intent.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 mode singleTask 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 E from 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 activity A, A has to use the method startActivityForResult(Intent) instead of method startActivity(Intent) to start B. When B defines its result via setResult(int resultCode) and finishes, the callback method onActivityResult() is called on A where 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 using startActivity(Intent). C may then define a result, which is forwarded back to A automatically 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, unless FLAG_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 that FLAG_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.

 

When calling startActivity, the new activity will live in the same screen stack (context) as the caller. Sometime, the caller may not have an activity context, for instance, starting an activity from Broadcastreceive.onReceive() method, or starting an activity from a service. In these cases, call startActivity will cause an exception of “Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK”, since the new activity does not have a context to live in.

To fix the issue, setting FLAG_ACTIVITY_NEW_TASK to the intent as follows, then a new context will be created for the new activity.

myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

By following just a few simple principles, says Jason Ostrander, author of Android UI Fundamentals: Develop & Design, Android developers can minimize user frustration with navigation in their apps.

Android development has exploded in recent years, making Android the largest smartphone OS by market share. As its user base increases, so do the number and quality of apps in the Google Play store. However, one area where developers still seem to struggle is with Android’s navigation model. Many apps fail to handle the nuances of activities and tasks properly, forcing users to endure endless back pressing to exit an app. To address this issue, this article presents seven tips for proper implementation of navigation in your Android app.

Learn from the Android Documentation

For many years, the Android documentation on navigation has been lacking. Luckily, Google has recently addressed this problem, and now there are many resources explaining how to navigate an Android app. In particular, the design guidelines and navigation training article provide clear case studies, working through the high-level application use case and following through with individual screens and the actions of the back and up buttons. You should read both of these resources and think carefully about your own application and how you expect people to use it. In addition, you should understand the basics of tasks and the back stack to leverage Android effectively.

Learn the Difference Between Up and Back

Android version 3.0 introduced a standardized navigation element called the ActionBar. This bar sits at the top of the screen and contains common actions and navigation elements, such as tabs. It also includes an icon on the left side that can be used as an up button (see Figure 1).

Figure 1 The up button as displayed in the Google Play app.

It’s important to understand the differences between the up and the back button. First, the up button is only relevant within your app. It will never take the user to a different application. In addition, the concept of up means that the user will return to a “logical” parent of the current activity. In the Gmail app, for example, pressing the up button while viewing an email message would take you to a list of email messages. If no parent exists, up should take the user to the “home” activity of your app.

The back button, on the other hand, moves back through the stack of recent activities. This is limited to those activities in the current task (more about that in a moment). Think of it like the browser’s back button. As a user navigates through an app and into others, the back button will take him backward through those activities, until he reaches the launcher. Once the user reaches the launcher, he can no longer press back.

Understand How Tasks Relate to the Back Stack

Tasks are collections of activities opened in chronological order by the user. A new task is created when the user opens an application from the launcher. Moving from one application to another generally doesn’t start a new task. Instead, the new activity becomes part of the existing task. For example, if you navigate from an existing app to the Google Maps app, the displayed map will be part of the existing app task. Pressing back from the map will take the user back to the original app.

Each Android task has an associated back stack. The back stack can seem complicated at first, but its implementation is firmly rooted in basic computer science. It’s just a stack. The elements in the stack are the screens of an app, called activities. As you navigate from one activity to another, the previous activity is pushed onto the stack. When you press the back button, the current activity (the current screen displayed to the user) is destroyed, and the activity at the top of the back stack is popped off and displayed to the user. If the back stack is empty, the user is taken back to the launcher.

An important point to note is that the system will not keep tasks around forever. When it needs resources, it will often clear the task and corresponding back stack. This is quite common, and you shouldn’t rely on any particular back stack state to exist for your app to function correctly. If you really need it, however, it’s possible to disable this behavior by setting the alwaysRetainTaskState attribute on an activity in your manifest.

Define the Proper Activity Attributes

By default, new activities are launched into existing tasks. This can be changed by setting activity attributes in your application manifest. For example, setting the launchMode attribute to singleTask ensures that only a single instance of an activity will be created:

<activity android:name=".TestActivity" 
    android:launchMode="singleTask">
</activity>

Now, intents for TestActivity will resume any task that already contains TestActivity, rather than creating another instance. If no instance exists, the system creates a new task containing TestActivity as normal.

Another useful activity attribute is taskAffinity. You can use taskAffinity to assign an activity to a particular task. By default, all activities in your application have the same affinity. Using the taskAffinity attribute, you can group them into separate tasks or even assign them to tasks in other applications. Here, the TestActivity is assigned to a specific task:

<activity android:name=".TestActivity" 
    android:taskAffinity="com.example.SecondTask">
</activity>

Read the Android documentation for a full explanation of the activity attributes available for controlling tasks.

Use Intent Flags

Activity attributes work well when you always want the same behavior for an activity. But sometimes you need control over a specific activity launch. For those cases, use Intent flags. Intents are used to launch activities on Android. You can set flags that control the task that will contain the activity. Flags exist to create a new activity, use an existing activity, or bring an existing instance of an activity to the front.

For example, it’s common to launch an activity when the user taps a notification. Often, apps will use the default intent flags, resulting in multiple copies of the same activity in the back stack. This forces the user to press back repeatedly while each instance of the activity is popped off the back stack. To fix this problem, set the flags Intent.FLAG_ACTIVITY_NEW_TASK and Intent.FLAG_ACTIVITY_CLEAR_TASK to switch to an existing instance of the activity and clear any other activities on top of it:

Intent intent = new Intent(context, TestActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

Note that in Android API version 11, the Intent class includes new static methods that create intents with the proper flags already set. Look here for an example.

Use the New Compatibility Package Classes

Google’s recent update to the compatibility package added two new utility classes to aid with app navigation. The first of these utilities is called NavUtils. It provides static methods for navigating up to the parent of the current activity, as declared in your application manifest. Here is an example manifest entry:

<activity android:name=".TestActivity" >
    <meta-data android:name="android.support.PARENT_ACTIVITY"
        android:value=".TestParentActivity">
    </meta-data>
</activity>

TestParentActivity is declared as a parent of TestActivity using a metadata element.

Now, in the TestActivity class, NavUtils is used to navigate up to TestParentActivity when the user presses the up button:

@Override
public boolean onOptionsItemSelected(android.view.MenuItem item) {
    if (item.getItemId() == android.R.id.home) {
        NavUtils.navigateUpFromSameTask(this);
        return true;
    }
    return super.onOptionsItemSelected(item);
}

The up button uses the same resource identifier as the ActionBar home icon, android.R.id.home. In addition to navigation, the NavUtils class provides a Boolean method specifying whether the current activity should display an up button.

The second class is TaskStackBuilder. This class can be used to construct a complete back stack, with a root activity and several activities on top of it. This back stack is created with a stack of intents. Calling startActivities() will create the back stack using the intents to create each activity and pushing it onto the stack.

TaskStackBuilder tsb = TaskStackBuilder.from(this);
tsb.addParentStack(this);
tsb.addNextIntent(new Intent(this, TestActivity.class));
tsb.addNextIntent(new Intent(this, TestActivity.class));
tsb.addNextIntent(new Intent(this, TestActivity.class));
tsb.startActivities();

This example adds three copies of the TestActivity to the current back stack. The user will have to press back three times to return to the existing activity. Using TaskStackBuilder, you can create tasks with entire back stack histories from scratch.

Test Your App

The last tip should be obvious: Test your app. Spend time testing all the different ways users can enter the activities in your application. If you provide an activity that can be reached from multiple places (a settings activity, for example), test navigating to it from throughout your app to see if it creates more than one instance. Think carefully about whether you want users to be pressing back through multiple copies of the same activity.

Test entering your application from notifications, widgets, and other apps. Be aware of possible memory issues. If you create multiple instances of a memory-intensive activity, it can cause an OutOfMemoryException. If your app provides a service that other apps might use, such as messaging, test how the user will move from other apps into your app. Follow the principle of least surprise. You never want users to be confused about why they’re seeing a particular activity.

Conclusion

With each new release, Android development becomes easier. But you still need to think about navigation basics if you want to provide the best possible experience. Remember, users expect your app to behave like other Android apps. It’s not enough to port existing user interfaces from other platforms. By learning the platform, thinking through the user interaction of your app, and thoroughly testing, you can create an app that delights its users.

 

The Android Application/Activity model is complex bordering on baroque. The following is not intended to be definitive, just sufficient to make it possible to explain how Intents are used in the context of Activities.

  • An Activity is an Application Component with an associated UI which is used to interact with the User.

  • An Application can comprise one or more Activities.

  • An Application can use one or more Activities to interact with the User but only one at a time.

  • An Application can use both Activities defined by itself and those defined by other Applications.

  • The Activities being used by an Application are usually grouped together in a task

  • The set of Activities in a task are represented as a stack.

  • The first Activity in the task is at the bottom of the stack.

  • The current Activity is at the top of the stack.

  • Activities start in response to Intents.

  • The Intents which an Activity will start in response to are specified using Intent Filters.

  • An Activity which is not started by another Activity will become the first Activity in a new task.

  • An Activity which is started by another Activity is by default in the same task as the Activity which started it.

  • When it finishes an Activity may return an Intent to the Activity which started it.

2.0 Associating Activities And Intents

The set of Intents an Activity can handle are specified by declaring intent-filter elements as children of the activity element used to declare the Activity itself in the Application’s manifest (the AndroidManifest.xml file).

When an Intent Filter is being declared in the context of an Activity the priority attribute cannot have a value greater than zero unless the Activity being declared is part of a System Application.

3.0 Starting Activities

3.1 Starting Activities And Intent Resolution

An Activity is started by specifying an Intent. Which Activity, if any, gets started for a given Intent is determined by the process of Intent resolution.

If an Intent explicitly specifies a component then the Intent resolves to that Application Component irrespective of whether it is actually an Activity or even whether it exists at all.

If an Intent does not specify a component then a search is made for all those Activities which satisfy two criteria

  1. one of their associated Intent Filters matches, as defined by the IntentFilter.match() method, the given Intent, and

  2. one of their associated Intent Filters specifies the android.intent.category.DEFAULT category

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

If more than one Activity is found then the Activity with the highest priority as defined by the associated Intent Filter(s) is chosen.

If all the Activities found have the same priority then the System will present the User with a choice and let them decide.

The methods used by the System to perform Intent resolution, or at least their functional equivalents, are available to Applications.

The Intent method

    public ComponentName resolveActivity(PackageManager pm)

will return the name of the Component to which the given Intent resolves, even if it is not an Activity or does not exist. In the case where the Intent resolves to multiple Activities with the same priority the name of the Activity which will be used to ask the User to choose will be returned.

The Intent method

    public ActivityInfo resolveActivityInfo(PackageManager pm, int flags)

behaves in the same way except that it will return null if the Intent resolves to a named Component which does not exist or is not an Activity.

These are both wrappers around a call to an implementation of the android.content.pm.PackageManager

    public abstract ResolveInfo resolveActivity (Intent intent, int flags)

method, with the flags argument either being, or including, the value specified by the class constant

   PackageManager.MATCH_DEFAULT_ONLY

The PackageManager class also defines the methid

    public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags)

An implementation of this method will return the set of all the Activities which are capable of being started in response to the given Intent.

3.2 Starting Activities And Intent Flags

The 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 FLAG_ACTIVITY_, are intended for use when an Intent is being used to start an Activity.

Some of the more interesting ones in this context are those that can be used for task management and Activity selection

3.2.1 FLAG_ACTIVITY_CLEAR_TOP

If the Intent resolves to an Activity in the current task the Activities above it on the stack are destroyed so that it is at the top of the stack, and it is re-used.

3.2.1.1 Example

There are four Activities in a task that have been started in the order One, Two, Three, Four.

Four starts an Activity using an Intent with the FLAG_ACTIVITY_CLEAR_TOP which resolves to Two.


3.2.2 FLAG_ACTIVITY_NEW_TASK

If the Intent resolves to an Activity which needs to be started it will be started in a new task.

3.2.3 FLAG_ACTIVITY_PREVIOUS_IS_TOP

For the purposes of deciding whether to re-use the top Activity the previous rather than the current Activity is considered to be top.[1]

3.2.4 FLAG_ACTIVITY_REORDER_TO_FRONT

If the Intent resolves to an Activity in the current task that Activity is moved to the top of the stack and re-used.

To be consistent with some of the other flags FLAG_ACTIVITY_REORDER_TO_TOP might have been a better name.

3.2.4.1 Example

There are four Activities in a task that have been started in the order One, Two, Three, Four.

Four starts an Activity using an Intent with the FLAG_ACTIVITY_REORDER_TO_FRONT which resolves to Two.

3.2.5 FLAG_ACTIVITY_SINGLE_TOP

If the Intent resolves to the Activity at the top of the current task stack it is re-used.

Note that in this context the top Activity is not necessarily the Activity at the top of the stack. See FLAG_ACTIVITY_PREVIOUS_IS_TOP.

3.3 Starting An Activity

An Activity can be started using an implementation of the android.content.Context

    public abstract void startActivity(Intent intent)

method.

If the given Intent cannot be resolved to an Activity or what it resolves to is not an Activity, or does not exist, an instance of the class

    android.content.ActivityNotFoundException 

will be thrown.

If the Application invoking the method has not been granted the permission necessary to start the Activity a SecurityException will be thrown.

If the method is not being invoked from an Activity the Intent must have the FLAG_ACTIVITY_NEW_TASK flag set.

3.3.1 The onNewIntent() Method

The effect of the startActivity() method is not always to start a new Activity.

In some cases an existing Activity will be re-used and its

    protected void onNewIntent(Intent intent)

method will be called with the starting Intent being passed as the intent argument.

For example, if the Activity SelfStart is declared as follows

    <activity 
        android:name  = "SelfStart" 
        android:label = "SelfStart">
        <intent-filter>
            <action 
                android:name = "xper.activity.ACTIVITY_SELF_START_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
            </intent-filter>
    </activity>

and its onNewIntent() method is defined to be

    protected void onNewIntent(Intent intent) 
    {
        System.out.println("SelfStart.onNewIntent()");
        System.out.println("\t" + intent);
        System.out.println("SelfStart.onNewIntent(): done");
    }

If, when it is the current Activity, it executes

    startActivity(
        new Intent(
                "xper.activity.ACTIVITY_SELF_START_INTENT").
            setFlags(
                Intent.FLAG_ACTIVITY_SINGLE_TOP));

then a new instance of SelfStart is not created and the onNewIntent() method prints

    SelfStart.onNewIntent()
        Intent { act=xper.activity.ACTIVITY_SELF_START_INTENT flg=0x20000000 cmp=xper.example.selfstart/.SelfStart }
    SelfStart.onNewIntent(): done

3.4 Starting An Activity And Getting A Result

The android.app.Activity method

    public void startActivityForResult(Intent intent, int requestCode)

will start an Activity in the same way as the startActivity() method above.

In addition if the requestCode argument is greater than or equal to zero then when the Activity that was started completes the caller’s

    protected void onActivityResult(int requestCode, int resultCode, Intent data)

method will be invoked.

The value of the requestCode argument will be the same as that passed to the orignal call to the startActivityForResult() method, so it can be used to identify the Activity from which the result originated.

In some cases the behaviour of the method is subtly different depending upon whether a result is requested or not.

For example, if the SelfStart Activity above executes the following

    startActivityForResult(
        new Intent(
                "xper.activity.ACTIVITY_SELF_START_INTENT").
            setFlags(
                 Intent.FLAG_ACTIVITY_SINGLE_TOP),
            -1);

the behaviour is the same as before.

However, if it executes

    startActivityForResult(
        new Intent(
                "xper.activity.ACTIVITY_SELF_START_INTENT").
            setFlags(
                Intent.FLAG_ACTIVITY_SINGLE_TOP),
        3);

then the Intent.FLAG_ACTIVITY_SINGLE_TOP flag is ignored and a second instance is created.

3.4.1 Returning A Result

An Activity can return a result code to the Activity that started it by calling the method

    public final void setResult(int resultCode)

before calling the finish() method.

It can return a result code and an Intent by calling the method

    public final void setResult(int resultCode, Intent data)

instead.

For example, assume two Activities Foo and Bar are part of the same Application.

Foo defines the following onActivityResult() method

    protected void onActivityResult(int requestCode, int resultCode, Intent data) 
    {
        System.out.println("Foo.onActivityResult(...)");
        System.out.println("\trequestCode == " + requestCode);
        System.out.println("\tresultCode  == " + resultCode) ;
        System.out.println("\tdata        == " + data);
        System.out.println("ActivityFoo.onActivityResult(...) done");
    }

Bar is declared in the Application’s manifest as follows

    <activity 
        android:name = "Bar">
        <intent-filter>
            <action 
                android:name = "xper.activity.ACTIVITY_BAR_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>

Foo starts Bar

    startActivityForResult(
        new Intent(
                "xper.activity.ACTIVITY_BAR_INTENT"), 
        1);

At some point Bar returns a result

    setResult(117, new Intent("xper.activity.ACTIVITY_BAR_RESULT_INTENT"));
    finish();

and Foo’s onActivityResult() method prints

    Foo.onActivityResult(...)
        requestCode == 1
        resultCode  == 117
        data        == Intent { act=xper.activity.ACTIVITY_BAR_RESULT_INTENT }
    ActivityFoo.onActivityResult(...) done

A result can be returned from an Activity defined in one Application to an Activity defined in another as long as they are in the same task.

If the effect of a call to startActivityForResult() is to start the Activity in a new task then the callers’s Activity.onActivityResult() method will be invoked immediately with a resultCode argument of zero and data argument of null.

If an Activity started using the startActivityForResult() method does not call either of the above methods before it finishes then the Activity.onActivityResult() method will be invoked with a resultCode argument of zero and data argument of null.

3.3.2 “Forwarding” A Result

If Activity One starts Activity Two using the startActivityForResult() method and Activity Two then starts Activity Three, Activity Two can specify that the result from Activity Three be returned to Activity One rather to itself. This is done by setting the flag defined by the Intent class constant FLAG_ACTIVITY_FORWARD_RESULT in the Intent used to start the Activity.

For example, assume the Activity Baz is also part of the Application in the example above.

Baz is declared in the Application’s manifest as follows

    <activity 
        android:name = "Baz">
        <intent-filter>
            <action 
                android:name = "xper.activity.ACTIVITY_BAZ_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>

Foo starts Bar as before

    startActivityForResult(
        new Intent(
                "xper.activity.ACTIVITY_BAR_INTENT"), 
        1);

At some point Bar then starts Baz

    startActivity(
        new Intent(
                "xper.activity.ACTIVITY_BAZ_INTENT").
            setFlags(
                Intent.FLAG_ACTIVITY_FORWARD_RESULT));

At some point Baz returns a result

    setResult(211, new Intent("xper.activity.ACTIVITY_BAZ_RESULT_INTENT"));
    finish();

At some point Bar returns a result as before

    setResult(117, new Intent("xper.activity.ACTIVITY_BAR_RESULT_INTENT"));
    finish();

and Foo’s onActivityResult() method then prints

    Foo.onActivityResult(...)
        requestCode == 1
        resultCode  == 211
        data        == Intent { act=xper.activity.ACTIVITY_BAZ_RESULT_INTENT }
    ActivityFoo.onActivityResult(...) done

Note that Bar starts Baz using the startActivity() method. It is not possible to use the FLAG_ACTIVITY_FORWARD_RESULT flag in conjunction with the startActivityForResult(). Doing so will result in an instance of

    android.util.AndroidRuntimeException

being thrown.

Note also that the result from Baz is not returned to Foo until Bar itself has finished, and that the resultCode and data returned by Baz override those returned by Bar but the requestCode is unchanged.

4.0 Starting An Activity: Variations On A Theme

The android.app.Activity class provides a couple of more specialized ways to start Activities.

4.1 Starting An Activity Only If Needed

The method

    public boolean startActivityIfNeeded(Intent intent, int requestCode)

is analagous to the startActivityForResult() method.

If the requestCode argument is greater than or equal to zero it works in the same way. The difference between them is that this method will only start a new Activity if it is needed.

A new Activity is not needed if the Intent specified by the intent argument resolves to the caller of the method, that is to the currently running Activity, and there may only be one instance of that Activity. In this case a new Activity is not started.

In any other circumstances a new Activity will be started.

The method returns true if a new Activity is started, and false otherwise, in which case the caller is expected to handle the Intent directly.

For example, assume an Application declares the following Activities

   <activity 
        android:name  = ".Humber"
        android:label = "@string/app_name">
        <intent-filter>
            <action 
                android:name = "android.intent.action.MAIN" />
            <category 
                android:name = "android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity 
        android:name       = "Thames" 
        android:launchMode = "singleTop">
        <intent-filter>
            <action 
                android:name = "xper.activity.ACTIVITY_THAMES_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>
    <activity 
        android:name = "Dover">
        <intent-filter>
            <action 
                android:name = "xper.activity.ACTIVITY_DOVER_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>

If Humber executes

    startActivityIfNeeded(
        new Intent(
                "xper.activity.ACTIVITY_THAMES_INTENT"), 
        -1);

the Activity Thames is started and the method returns true.

If Thames executes

    startActivityIfNeeded(
        new Intent(
                "xper.activity.ACTIVITY_THAMES_INTENT"), 
        -1);

it is not necessary to start a new instance of Thames and the method returns false.

If Thames executes

    startActivityIfNeeded(
        new Intent(
                "xper.activity.ACTIVITY_DOVER_INTENT"), 
        -1);

the Activity Dover is started and the method returns true.

If Dover executes

    startActivityIfNeeded(
        new Intent(
                "xper.activity.ACTIVITY_DOVER_INTENT"), 
        -1);

a second instance of the Dover Activity is started, because by default there may be multiple instances of it, and the method returns true.

Passing a requestCode greater than or equal to zero results in behaviour identical to startActivityForResult() with the same arguments.

For example, if Thames executes

    startActivityIfNeeded(
        new Intent(
                "xper.activity.ACTIVITY_THAMES_INTENT"), 
        2);

then a second instance of the Thames Activity is started, and the method returns true.

Given that in these circumstances it is apparently always necessary to start an Activity it is not clear why the method needs to take a second argument at all.

If an Activity needs to be started and one cannot be found an ActivityNotFoundException will be thrown.

4.2 Starting The Next Matching Method

If the method

    public boolean startNextMatchingActivity(Intent intent)

is called with the Intent used to start the current Activity, then the method will start the next Activity, if any, capable of being started in response to that Intent.

The Intent passed as the intent argument does not have to be identical to the one that started the current Activity, it can differ in the extras that it specifies, which means it is possible to pass data between Activities started in this mode.

The method returns true if an Activity has been started, in which case the current Activity should then call the finish() method, otherwise false.

In this context next means the Activity, if any, after the current Activity, in the list of Activities constructed during Intent resolution.

For example, if there are three Activities each declared in a different Application as follows

    <activity 
        android:name  = "Plymouth" 
        android:label = "Plymouth">
        <intent-filter 
            android:priority = "-2">
            <action 
                android:name = "xper.activity.CHANNEL_ACTIVITY_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>
	
    ...
	
    <activity 
        android:name  = "Portland" 
        android:label = "Portland">
        <intent-filter 
            android:priority = "-1">
            <action 
                android:name = "xper.activity.CHANNEL_ACTIVITY_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>
		
    ...
	
    <activity 
        android:name  = "Wight" 
        android:label = "Wight">
        <intent-filter>
            <action 
                android:name = "xper.activity.CHANNEL_ACTIVITY_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>

then, when used to start an Activity, an Intent with the action xper.activity.CHANNEL_ACTIVITY_INTENT will be resolved to these Activities in the order

  1. Wight

  2. Portland

  3. Plymouth

If some other Activity executes

    startActivity(
        new Intent(
                "xper.activity.CHANNEL_ACTIVITY_INTENT"))

then Wight will be started.

If Wight executes

    startNextMatchingActivity(
        new Intent(
                "xper.activity.CHANNEL_ACTIVITY_INTENT"))

the Portland will be started, and the method will return true.

If Portland executes

    startNextMatchingActivity(
        new Intent(
                "xper.activity.CHANNEL_ACTIVITY_INTENT"))

the Plymouth will be started, and the method will return true.

If Plymouth executes

    startNextMatchingActivity(
        new Intent(
                "xper.activity.CHANNEL_ACTIVITY_INTENT"))

the method will return false as there are no other Activities to start.

The behaviour is the same regardless of which Activity is started first.

If Plymouth is started first and it executes

    startNextMatchingActivity(
        new Intent(
                "xper.activity.CHANNEL_ACTIVITY_INTENT"))

the method will still return false. It will not wrap around to Wight.

5.0 Activity Implementation And Intent Access

An Activity can access the Intent which was used to start it by calling the method

    public Intent getIntent()

By default the Intent returned does not reflect any subsequent calls to the Activity’s onNewIntent() method.

However, an Activity can set the Intent which will be returned by calling the

    public void setIntent(Intent newIntent)

method.

6.0 Activity Intent Filters And Intent Categories

The System pre-defines a number of Intent categories. When these are specified in Intent Filters associated with Activities the System treats some of them as declarations which apply to those Activities or to the Applications that declare those Activities.

For example, if the category defined by the Intent() class constant

    CATEGORY_HOME

with the value

   "android.intent.category.HOME"

is specified, then the Application containing the declaration is treated by the System as the home Application and is started automatically when the System starts up. If there are multiple such Applications then the user may be offered the choice of which one to start as the home Application.

If the the category defined by the Intent() class constant

    CATEGORY_LAUNCHER

with the value

   "android.intent.category.LAUNCHER"

is specified, then the System will make it possible for the User to launch the Application containing the declaration via the UI in some way.

There is no way of knowing whether the overloading of Intent categories in this context was originally part of the design or whether its just something that somebody hit upon afterwards.

One slight problem with it is that it is not possible to know simply by looking at an Application’s manifest whether the Application or one or more of its Activities might have some special status or mode of behaviour, unless you are familiar with all the System pre-defined Intent categories and their semantics.

7.0 Starting A Specific Activity

As mentioned above if an Intent specifies a Component explicitly then the Intent resolves to that Component regardless of anything else specified by the Intent.

This makes it possible to use an Intent to start any Activity given only its ComponentName as long as the Application doing so has the necessary permission.

For example if the Forties Activity is declared in the xper.example.activity.forties Application package as follows

    <activity 
        android:name  = "Forties" 
        android:label = "Forties">
        <intent-filter>
            <action 
                android:name = "xper.activity.ACTIVITY_FORTIES_INTENT"/>
            <category 
                android:name = "android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>

then it can be started as follows

    startActivity(
        new Intent(
                "xper.activity.ACTIVITY_CROMARTY_INTENT").
            setClassName(
                "xper.example.activity.forties", 
                "xper.example.activity.forties.Forties"));

despite the fact that the xper.activity.ACTIVITY_CROMARTY_INTENT action does not appear in its Intent Filter.

8.0 “Anonymous” Activities

It is possible to specify an Activity without any Intent Filters, for example,

    <activity android:name="AnonymousActivity"/>

This Activity cannot be accessed from outside the Application which declared it, but from within that Application it can still be started by specifying it explicitly as described above, for example,

    startActivity(new Intent(this, AnonymousActivity.class));

If an Activity is internal to an Application and only ever needs to be started explicitly then this is actually useful functionality especially since it avoids the expense of full on Intent resolution.

Perhaps less usefully it is also possible to make an Activity without any Intent Filters accessible outside the Application which declared it by explicitly exporting it. For example,

    <activity 
        android:name     = "PublicAnonymousActivity"
        android:exported = "true"
    />

Notes

[1] It is not clear in what circumstances the FLAG_ACTIVITY_PREVIOUS_IS_TOP Intent flag actually works.

The flag is handled in the com.android.server.am.ActivityStack method

    final int startActivityUncheckedLocked(
                  ActivityRecord r,
                  ActivityRecord sourceRecord, 
                  Uri[]          grantedUriPermissions,
                  int            grantedMode, 
                  boolean        onlyIfNeeded, 
                  boolean        doResume)

(lines 2039-2385).

The ActivityStack implementation can be found in the file

    $(ANDROID_SRC)/frameworks/base/services/java/com/android/server/am/ActivityStack.java

At line 2060 the notTop variable is initialized as follows (code slightly reformatted)

    ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;

At line 2259 the the top variable is initialized as follows

    ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);

This is immediately preceeded by the following comment(s) (lines 2256-2258)

    // If the activity being launched is the same as the one currently
    // at the top, then we need to check if it should only be launched
    // once.

The topRunningNonDelayedActivityLocked() method is defined as follows (lines 332-342)

    final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
        int i = mHistory.size()-1;
        while (i >= 0) {
            ActivityRecord r = (ActivityRecord)mHistory.get(i);
            if (!r.finishing && !r.delayedResume && r != notTop) {
                 return r;
            }
            i--;
        }
        return null;
    }

so the top Activity as returned by this method is the first ActivityRecord, starting from the back of the list of ActivityRecords held in the mHistory instance variable, which is not finishing, is not in the delayedResume state, and is not the ActivityRecord passed in as the notTop
argument.

If the notTop argument is null the expression

    r != notTop

is necessarily true.

If the notTop argument is not null then the expression

    r != notTop

may be false, in which case the current ActivityRecord r will be skipped.

Given the use of the != operator for the expression

    r != notTop

to be false, r and notTop have to be the same ActivityRecord object, and hence the ActivityRecord passed as the notTop argument must be in list of ActivityRecords held in mHistory.

This is necessarily true regardless of what the ActivityRecord passed as the notTop argument represents or what mHistory represents.

When invoked from the startActivityUncheckedLocked() topRunningNonDelayedActivityLocked() is passed the notTop variable.

When the FLAG_ACTIVITY_PREVIOUS_IS_TOP is set the notTop variable is the ActivityRecord passed as the argument r.

The method startActivityUncheckedLocked() is invoked by the

    final int startActivityLocked(
                 IApplicationThread caller,
                 Intent             intent, 
                 String             resolvedType,
                 Uri[]              grantedUriPermissions,
                 int                grantedMode, 
                 ActivityInfo       aInfo, 
                 IBinder            resultTo,
                 String             resultWho, 
                 int                requestCode,
                 int                callingPid, 
                 int                callingUid, 
                 boolean            onlyIfNeeded,
                 boolean            componentSpecified)

method [sic] (lines 1877-2037).

The ActivityRecord r is constructed (line 2002) but it is not added to the list of ActivityRecords held in mHistory before being passed to startActivityUncheckedLocked() (line 2035).

Nor, in the case when FLAG_ACTIVITY_PREVIOUS_IS_TOP is set, does it appear to have been added before being passed to topRunningNonDelayedActivityLocked() in the guise of the notTop variable, which implies that passing it cannot have any effect at all on the behaviour of that method.

This may explain why it does not seem to be possible to devise an example where setting the FLAG_ACTIVITY_PREVIOUS_IS_TOP flag has any effect.

 

 
The VideoView class included in the Android SDK provides a very convenient wrapper of the Surface View class. It takes care of computing its measurement from the source video so that it can be used in any layout manager.

By default  Android will scale the video to fit the VideoView but keeping the video aspect ratio. That means that depending on the device’s screen size there might be some black spaces left around the video the video since the it’s not filling the screen.

Depending on what you are looking for, you might actually want to completely fill the parent container area even if that means to stretch the video to fill the gaps (in other words, altering the aspect ratio of the video).

Well, turns out that even though the VideoView doesn’t provide an explicit way to specify the filling mode, you can achieve the same result by wrapping the video view in a Relative Layout and setting the VideoView alignments to match the parent’s boundaries:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <VideoView android:id="@+id/videoViewRelative"
         android:layout_alignParentTop="true"
         android:layout_alignParentBottom="true"
         android:layout_alignParentLeft="true"
         android:layout_alignParentRight="true"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent">
    </VideoView>
    
</RelativeLayout>

Here is an example of two different layouts, one using a Linear Layout to wrap a VideoView and the other one using a Relative Layout: 

LinearLayour wrapper
RelativeLayout wrapper
 

So, whether you want to keep the aspect ratio of a video or stretch it to fill its parent area, using the right layout manager can get the job done. 

Hope this helps!