使用launchMode="singleTask"的onActivityResult函数?

24

当您尝试为具有launchMode="singleTask"Activity启动startActivityForResult时,它将不会返回任何值与onActivityResult,而当您设置launchMode="standard"时; 一切正常工作,但是系统要求这个Activity必须是singleTask,有没有什么方法可以解决这个问题?

5个回答

49

startActivityForResult 的文档中写道:

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.

看起来似乎没有办法绕过这个问题。

如果您是被调用 Activity 的开发者,那么您可以让它在一些结果可用时发送广播。然后,调用 Activity 可以侦听此广播。


1
那么如何防止在onClick中创建多个实例(例如ListView)? - Imon

40
答案:答案显示在ActivityStackSupervisor类的startActivityUncheckedLocked函数中。在Android 5.x之前,启动活动时,首先检查launchMode并将FLAG_ACTIVITY_NEW_TASK添加到launchFlags中,如果launchModesingleTasksingleInstance。如果活动的launchFlags包含FLAG_ACTIVITY_NEW_TASK,它将立即发送一个取消,让新任务继续正常启动,而不依赖于其发起者。
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;
}

但在Android 5.x中,这种情况已经发生了变化,如下所示:

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;
}

这就是为什么在 Android 5.x 中,即使你将 launchMode 设置为 singleTasksingleInstanceonActivityResult 仍然有效的原因。


7

彼得·克内戈(Peter Knego)说:

另外,这个问题在 Android 5.1 中似乎可以解决,但在 Android 4.4.4 中不行。

也就是说,onActivityResult 方法会被触发。


是的,我也观察到它在5.0及以上版本中运行,而不是在4.4.4中。 - Mahesh

2
为了澄清一些答案:

API's < Android 5.x:

startActivityForResult() 结合 singleTasksingleInstance 的 launchmodes 不起作用。 onActivityResult() 将立即被调用,结果代码为 Activity.RESULT_CANCELED

API's >= Android 5.x:

startActivityForResult() 起作用,但是 singleTasksingleInstance 的 launchmodes 基本上会被忽略,这意味着该活动将启动到同一任务中,不会创建新的任务

如果您想验证此内容,请运行 adb shell dumpsys activity activities。我只希望系统至少能向我显示一个警告。

@GGCoke 的代码片段 发布 中也反映了这种变化。


-1

我知道这有点晚了,但是你可以在onNewIntent()方法中实现OnActivityResult的效果,因为这是你的singleTask活动。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接