Mobile Development 23 min read

Exploring FLAG_ACTIVITY_NEW_TASK and Activity Stack Validation in Android

The article investigates a real‑world Android navigation bug by dissecting how FLAG_ACTIVITY_NEW_TASK interacts with launch modes, task affinities and intent construction, revealing that the flag does not always behave like singleTask, that START_DELIVERED_TO_TOP may fail to deliver new intents, and offering debugging steps and practical fixes.

vivo Internet Technology
vivo Internet Technology
vivo Internet Technology
Exploring FLAG_ACTIVITY_NEW_TASK and Activity Stack Validation in Android

This article starts from a real‑world issue and uses the FLAG_ACTIVITY_NEW_TASK flag as a entry point to investigate the crucial stack verification step that occurs before an Android Activity is launched.

Problem and background : Inter‑application navigation is a common way to achieve overall system consistency, but using the usual startActivity() method can sometimes fail. In the described business case, clicking a button should jump to another app, yet nothing happens. The possible guesses (missing target Activity, missing export, permission problems, wrong action/URI, etc.) turn out to be hidden by the combination of flag , launchMode and Intent attributes.

The article first lists the AndroidManifest configurations of three applications (A, B, C). All activities are exported and use the standard launch mode:

<!-- Application A -->

Jumping code snippets illustrate how the flag is used:

private void jumpTo_B_Activity2_ByAction_NewTask() {
    Intent intent = new Intent();
    intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);
}
private void jumpTo_C_Activity3_ByAction_NoTask() {
    Intent intent = new Intent();
    intent.setAction("com.zkp.task.ACTION_TO_C_PAGE3");
    startActivity(intent);
}
private void jumpTo_B_Activity2_ByAction_NewTask() { // from C‑3
    Intent intent = new Intent();
    intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);
}

Initial analysis : Two observations are made – (1) jumping from C‑3 to B‑2 works only when the previous A‑1→B‑2 jump has not occurred; (2) both A‑1→B‑2 and C‑3→B‑2 set FLAG_ACTIVITY_NEW_TASK . The hypothesis is that the stack state is the root cause, and the article prints the stack before each jump (images omitted).

Scenario expansion and verification : Several scenarios (0‑4) are built to test the behavior.

Scenario 0 : Replace the cross‑app jump B‑2→C‑3 with an intra‑app jump B‑2→B‑3.

public static void jumpTo_B_3_ByAction_Null(Context context) {
    Intent intent = new Intent();
    intent.setAction("com.zkp.task.ACTION_TO_B_PAGE3");
    context.startActivity(intent);
}

Scenario 1 : From a different task (C‑4) jump back to B‑2 using the same NEW_TASK flag.

public static void jumpTo_C_4_ByAction_New(Context context) {
    Intent intent = new Intent("com.zkp.task.ACTION_TO_C_PAGE4");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
}

public static void jumpTo_B_2_ByAction_New(Context context) {
    Intent intent = new Intent();
    intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
}

Scenario 2 : Change the C‑4→B‑2 jump to use setClassName instead of an action.

public static void jumpTo_B_2_ByPath_New(Context context) {
    Intent intent = new Intent();
    intent.setClassName("com.zkp.b", "com.zkp.b.Activity2"); // direct class name
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
}

Scenario 3 : From C‑4 jump to B‑3 (still using an action) with NEW_TASK .

public static void jumpTo_B_3_ByAction_New(Context context) {
    Intent intent = new Intent();
    intent.setAction("com.zkp.task.ACTION_TO_B_PAGE3");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
}

Scenario 4 : Give B‑3 a separate taskAffinity to force a new task.

<activity
    android:name=".Activity3"
    android:exported="true"
    android:taskAffinity="b3.task">
    <intent-filter>
        <action android:name="com.zkp.task.ACTION_TO_B_PAGE3" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Each scenario is executed and the observed results (expected vs. actual) are described, showing that the official documentation for FLAG_ACTIVITY_NEW_TASK does not always match reality, especially when the launch mode, taskAffinity , or the way the Intent is built changes.

Source‑code debugging : The article outlines how to debug the Android 12 source, noting that the result code START_DELIVERED_TO_TOP (3) means the activity was not really started but the intent was delivered to the existing top activity. However, in many cases the top activity never receives onNewIntent() , indicating a mismatch between the documented behavior and the actual implementation.

Detailed launch flow : The key classes and methods are listed (e.g., ActivityStarter.execute() , startActivityInner() , getReusableTask() , recycleTask() , complyActivityFlags() ). Important variables mMovedToFront and mAddingToTask are explained, showing how they determine whether a task is brought to front or a new activity instance is added.

Problem‑fix suggestions :

Modify the flag – add FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_CLEAR_TOP , or simply omit NEW_TASK .

Change the intent – use setClassName or adjust the action attributes as in Scenario 2.

Finish the source activity before starting the next one – call finish() before startActivity() to avoid stale ActivityRecord data.

Remaining issue : The article explains why onNewIntent() is not invoked – the conditions inside complyActivityFlags() that would trigger deliverNewIntent() are not satisfied, and deliverToCurrentTopIfNeeded() also does not start a new intent.

Conclusion : The investigation reveals that FLAG_ACTIVITY_NEW_TASK is not equivalent to singleTask in all cases, that START_DELIVERED_TO_TOP does not always forward the new intent, and that subtle differences in how an activity is launched (action vs. class name, task affinity, additional flags) can dramatically change the outcome. Developers need to consider the whole combination of task history, flag settings, launch mode, and intent content when dealing with stack‑related code.

AndroidStackActivityIntentLaunchModeFLAG_ACTIVITY_NEW_TASKtask
vivo Internet Technology
Written by

vivo Internet Technology

Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.