24

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?

Mohammad Ersan
  • 11,926
  • 8
  • 48
  • 72

5 Answers5

46

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.

Peter Knego
  • 78,855
  • 10
  • 118
  • 147
  • So how to prevent creation of multiple instances of (for example a listview) in onClick? – Imon Oct 02 '12 at 14:26
40

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.

noelicus
  • 12,864
  • 2
  • 82
  • 94
GGCoke
  • 566
  • 4
  • 4
7

What @Peter Knego says

plus

it seems to be working in 5.1, not in 4.4.4

meaning that onActivityResult fires

AndroidGecko
  • 13,296
  • 3
  • 31
  • 47
1

To clarify some of the answers:

API's < Android 5.x:

startActivityForResult() in combination with launchmodes singleTask or singleInstance doesn't work. onActivityResult() will get called immediately with a result code of Activity.RESULT_CANCELED.

API's >= Android 5.x:

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.

MattSchmatt
  • 528
  • 4
  • 15
-1

I know this is quite late but you can have OnActivityResult kind of effect on the onNewIntent() method because this is your singleTask activity.

android_griezmann
  • 3,217
  • 3
  • 13
  • 39