Reputation: 12444
When you try to startActivityForResult
for Activity
that has launchMode="singleTask"
; it will not return any values with onActivityResult
,and when you set launchMode="standard"
; everything works fine, but the system requirements says this Activity
must be singleTask
, is there anyway to solve this?
Upvotes: 25
Views: 18297
Reputation: 900
To clarify some of the answers:
startActivityForResult()
in combination with launchmodes singleTask
or singleInstance
doesn't work. onActivityResult()
will get called immediately with a result code of Activity.RESULT_CANCELED
.
startActivityForResult()
works but launchmodes singleTask
or singleInstance
are basically ignored, meaning the activity is launched into the same task and no new task is created.
If you want to verify this run adb shell dumpsys activity activities
. I only wish the system would at least have shown me a warning about that.
This change is also reflected in the bits of code @GGCoke has posted.
Upvotes: 2
Reputation: 556
The answer shows in function startActivityUncheckedLocked
of class ActivityStackSupervisor
.
Before Android 5.x, when starting an activity, it will check launchMode first and add FLAG_ACTIVITY_NEW_TASK
to launchFlags if launchMode is singleTask or singleInstance. If the activity's launchFlags contains FLAG_ACTIVITY_NEW_TASK
, it will send back a cancel immediately and let the new task continue launched as normal without a dependency on its originator.
if (sourceRecord == null) {
// This activity is not being started from another... in this
// case we -always- start a new task.
if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
"Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
} else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
// The original activity who is starting us is running as a single
// instance... this new activity it is starting must go on its
// own task.
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
} else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
// The activity being started is a single instance... it always
// gets launched into its own task.
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
// ......
if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
// For whatever reason this activity is being launched into a new
// task... yet the caller has requested a result back. Well, that
// is pretty messed up, so instead immediately send back a cancel
// and let the new task continue launched as normal without a
// dependency on its originator.
Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
r.resultTo.task.stack.sendActivityResultLocked(-1,
r.resultTo, r.resultWho, r.requestCode,
Activity.RESULT_CANCELED, null);
r.resultTo = null;
}
But in Android 5.x, this was changed as below:
final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
int launchFlags = intent.getFlags();
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
(launchSingleInstance || launchSingleTask)) {
// We have a conflict between the Intent and the Activity manifest, manifest wins.
Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
"\"singleInstance\" or \"singleTask\"");
launchFlags &=
~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
} else {
switch (r.info.documentLaunchMode) {
case ActivityInfo.DOCUMENT_LAUNCH_NONE:
break;
case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
launchFlags &= ~Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
break;
}
}
final boolean launchTaskBehind = r.mLaunchTaskBehind
&& !launchSingleTask && !launchSingleInstance
&& (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
// For whatever reason this activity is being launched into a new
// task... yet the caller has requested a result back. Well, that
// is pretty messed up, so instead immediately send back a cancel
// and let the new task continue launched as normal without a
// dependency on its originator.
Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
r.resultTo.task.stack.sendActivityResultLocked(-1,
r.resultTo, r.resultWho, r.requestCode,
Activity.RESULT_CANCELED, null);
r.resultTo = null;
}
That's why onActivityResult
works in Android 5.x even you set launchMode to singleTask
or singleInstance
.
Upvotes: 39
Reputation: 3887
I know this is quite late but you can have OnActivityResult kind of effect on the onNewIntent() method because this is your singleTask activity.
Upvotes: -1
Reputation: 14174
What @Peter Knego says
plus
it seems to be working in 5.1, not in 4.4.4
meaning that onActivityResult fires
Upvotes: 7
Reputation: 80340
The docs of the startActivityForResult
say:
For example, if the activity you are launching uses the singleTask launch mode,
it will not run in your task and thus you will immediately receive a cancel result.
It seems there is no way to get around this.
If you are the developer of called Activity, then you can have it send a broadcast when some result is available. The calling Activity can then list to this broadcasts.
Upvotes: 51