In-depth Source Code Investigation of FLAG_ACTIVITY_NEW_TASK Causing Activity Launch Failure
This article analyzes why adding the FLAG_ACTIVITY_NEW_TASK flag can prevent an Android Activity from starting, examines the underlying framework source code—including startActivityInner, getReusableTask, recycleTask, and complyActivityFlags—and provides practical solutions to resolve the launch failure.
In-depth Source Code Investigation of FLAG_ACTIVITY_NEW_TASK Causing Activity Launch Failure
Android provides four launch modes for Activities, but the actual start behavior is also heavily influenced by Intent flags such as android:launchMode and android:taskAffinity . When a Service tries to start an Activity, the framework requires the FLAG_ACTIVITY_NEW_TASK flag, and the interaction of this flag with launch modes can lead to situations where an Activity never gets created.
Basic LaunchMode Behavior
standard : always creates a new Activity instance.
singleTop : reuses the top Activity and calls onNewIntent() if it is already at the top.
singleTask : clears Activities above an existing instance and calls onNewIntent() .
singleInstance : runs in its own task and also calls onNewIntent() when reused.
These modes work only when no additional flags are added and taskAffinity is unchanged.
Service Starting an Activity
When a Service starts an Activity, the framework throws an AndroidRuntimeException if the FLAG_ACTIVITY_NEW_TASK flag is missing, because a non‑Activity context may not have an existing task stack.
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& (targetSdkVersion < Build.VERSION_CODES.N || targetSdkVersion >= Build.VERSION_CODES.P)
&& (options == null || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}Unable to Start a standard Activity
Reproduction Steps
Service adds FLAG_ACTIVITY_NEW_TASK and starts AActivity .
AActivity starts BActivity .
Service again adds FLAG_ACTIVITY_NEW_TASK and starts AActivity .
Result: AActivity never receives onCreate , onResume or onNewIntent . BActivity remains unchanged.
Cause Speculation
Removing the finish() call before the second launch makes AActivity start successfully, indicating that the combination of FLAG_ACTIVITY_NEW_TASK , the standard launch mode, and the existing task stack prevents a new instance from being created.
Source Code Tracing
startActivityInner
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity, NeededUriGrants intentGrants) {
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor, restrictedBgActivity);
// compute launching flags
computeLaunchingTaskFlags();
// find reusable task
final Task reusedTask = getReusableTask();
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
final boolean newTask = targetTask == null;
mTargetTask = targetTask;
computeLaunchParams(r, sourceRecord, targetTask);
int startResult = isAllowedToStart(r, newTask, targetTask);
if (startResult != START_SUCCESS) {
return startResult;
}
// ... further processing ...
}getReusableTask
private Task getReusableTask() {
// Conditions for putting into an existing task:
// 1. Flags contain FLAG_ACTIVITY_NEW_TASK but not FLAG_ACTIVITY_MULTIPLE_TASK
// 2. launchMode is singleInstance or singleTask
boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
if (putIntoExistingTask) {
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info,
mStartActivity.isActivityTypeHome());
} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info,
!(LAUNCH_SINGLE_TASK == mLaunchMode));
} else {
intentActivity = mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);
}
}
return intentActivity != null ? intentActivity.getTask() : null;
}recycleTask
int recycleTask(Task targetTask, ActivityRecord targetTaskTop, Task reusedTask,
NeededUriGrants intentGrants) {
if (targetTask.mUserId != mStartActivity.mUserId) {
mTargetStack = targetTask.getStack();
mAddingToTask = true;
return START_SUCCESS;
}
// ... many branches ...
if ((mLaunchFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
if (!mMovedToFront && mDoResume) {
mTargetStack.moveToFront("intentActivityFound");
}
resumeTargetStackIfNeeded();
return START_RETURN_INTENT_TO_CALLER;
}
// ...
if (mAddingToTask) {
return START_SUCCESS;
}
// ...
return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
}complyActivityFlags
private void complyActivityFlags(Task targetTask, ActivityRecord reusedActivity,
NeededUriGrants intentGrants) {
ActivityRecord targetTaskTop = targetTask.getTopNonFinishingActivity();
final boolean resetTask = reusedActivity != null &&
(mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0;
if (resetTask) {
targetTaskTop = mTargetStack.resetTaskIfNeeded(targetTaskTop, mStartActivity);
}
if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) ==
(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
targetTask.performClearTaskLocked();
targetTask.setIntent(mStartActivity);
mAddingToTask = true;
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
final ActivityRecord top = targetTask.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);
if (top != null) {
if (top.isRootOfTask()) {
top.getTask().setIntent(mStartActivity);
}
deliverNewIntent(top, intentGrants);
} else {
mAddingToTask = true;
}
} else if ((mLaunchFlags & FLAG_ACTIVITY_REORDER_TO_FRONT) != 0 &&
!mAddingToTask) {
ActivityRecord act = targetTask.findActivityInHistory(mStartActivity.mActivityComponent);
if (act != null) {
Task task = act.getTask();
task.moveActivityToFrontLocked(act);
act.updateOptionsLocked(mOptions);
deliverNewIntent(act, intentGrants);
mTargetStack.mLastPausedActivity = null;
} else {
mAddingToTask = true;
}
} else if (mStartActivity.mActivityComponent.equals(targetTask.realActivity)) {
if (targetTask == mInTask) {
// same task, do nothing
} else if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 ||
LAUNCH_SINGLE_TOP == mLaunchMode &&
targetTaskTop.mActivityComponent.equals(mStartActivity.mActivityComponent) &&
mStartActivity.resultTo == null) {
if (targetTaskTop.isRootOfTask()) {
targetTaskTop.getTask().setIntent(mStartActivity);
}
deliverNewIntent(targetTaskTop, intentGrants);
} else if (!targetTask.isSameIntentFilter(mStartActivity)) {
mAddingToTask = true;
} else if (reusedActivity == null) {
mAddingToTask = true;
}
} else if (!resetTask) {
mAddingToTask = true;
} else if (!targetTask.rootWasReset) {
targetTask.setIntent(mStartActivity);
}
}Reason Summary
The combination of FLAG_ACTIVITY_NEW_TASK with the standard launch mode, together with the current task‑stack state (especially after a finish() call), leads the framework to select a reusable task, skip the creation of a new Activity, and ultimately return START_DELIVERED_TO_TOP without invoking any lifecycle callbacks.
Solutions
Force the framework to treat the launch as a new task by adding FLAG_ACTIVITY_MULTIPLE_TASK together with FLAG_ACTIVITY_NEW_TASK , or by removing FLAG_ACTIVITY_NEW_TASK when it is not required.
Ensure that complyActivityFlags reaches a branch where mAddingToTask becomes true , e.g., by using FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_CLEAR_TOP as appropriate.
Avoid calling finish() on the previous Activity if it would change the task’s root component, or adjust the launch mode of the target Activity (e.g., use singleTask instead of standard ).
Reproducing the Anomaly
Steps
Service starts AActivity with FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK and launchMode="singleInstance" .
AActivity (optionally after finish() ) starts BActivity with a plain startActivity() .
Service repeats step 1, then step 2.
Result: BActivity is not recreated; instead onRestart() of the original instance is invoked.
Further Source Tracing
The framework automatically adds FLAG_ACTIVITY_NEW_TASK in three situations (no source Activity, source launchMode is singleInstance , or target launchMode is singleInstance / singleTask ), which explains why AActivity receives the flag and triggers the problematic path.
private void computeLaunchingTaskFlags() {
if (mInTask == null) {
if (mSourceRecord == null) {
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
"Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
} else if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}
}Solution for the Second Scenario
Start BActivity with FLAG_ACTIVITY_MULTIPLE_TASK to prevent the framework from reusing the existing task and to force a fresh instance.
finish();
startActivity(
Intent(this, BActivity::class.java)
.apply { addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) }
);Conclusion
Activity launching in Android is highly complex; a single flag such as FLAG_ACTIVITY_NEW_TASK can interact with launch modes, task affinity, and the current task stack to produce hard‑to‑detect bugs. By tracing the framework source (especially startActivityInner , getReusableTask , recycleTask , and complyActivityFlags ) and adjusting flags or launch modes, developers can resolve these elusive launch failures.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.