希望有人能帮我弄清楚,如果不是解决方案,至少是一个行为的解释。

存在的问题:

在某些设备上,按下启动器图标会导致当前任务被恢复,在其他设备上则会导致初始启动意图被触发(有效地重新启动应用程序)。为什么会发生这种情况?

细节:

当你按下“启动器图标”时,应用程序正常启动——也就是说,我假设,一个Intent以你的第一个Activity的名称启动,动作为android.intent.action.MAIN,类别为android.intent.category.LAUNCHER。但情况并非总是如此:

在大多数设备上,如果你在应用程序已经运行后按下启动器图标,该进程中当前运行的Activity将恢复(而不是初始Activity)。它以相同的方式恢复,就像你从操作系统菜单中的“最近任务”中选择它一样。这是我想在所有设备上的行为。

然而,在选定的其他设备上,会发生不同的行为:

On the Motorola Xoom, when you press the launcher icon, the App will always start the initial launch Activity regardless of what is currently running. I assume that the launcher icons always start the "LAUNCHER" intent. On the Samsung Tab 2, when you press the launcher icon, if you have just installed the app, it will always launch the initial Activity (Same as the Xoom) - however, after you restart the device after the install, the launcher icon will instead resume the app. I assume that these devices add "installed apps" into a lookup table on device startup which allow the launcher icons to correctly resume running tasks?

我读过很多答案,听起来类似于我的问题,但简单地添加android:alwaysRetainTaskState="true"或使用launchMode="singleTop"的活动不是答案。

编辑:

在这个应用程序最近一次启动后,我们发现在第一次重启后,这种行为已经开始在所有设备上发生。这对我来说似乎很疯狂,但通过重新启动过程,我实际上找不到哪里出了问题。


当前回答

啊哈!(tldr;请参阅底部粗体的语句)

我发现问题了……我认为。

我先从一个假设开始。当你按下启动器时,它会启动默认的Activity,或者,如果之前启动的Task是打开的,它会把它带到前面。换句话说,如果在导航的任何阶段,你创建了一个新的任务,并完成了旧的任务,启动器现在将不再继续你的应用程序。

如果这个假设是正确的,我很确定这应该是一个错误,因为每个任务都在同一个过程中,并且与创建的第一个任务一样是有效的简历候选人。

然后,我的问题通过从几个intent中删除这些标志来解决:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK );

虽然很明显FLAG_ACTIVITY_NEW_TASK创建了一个新Task,但我没有意识到上面的假设是有效的。我确实认为这是一个罪魁祸首,并将其移除进行测试,但我仍然有问题,所以我驳回了它。但是,我仍然具备以下条件:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)

我的启动画面使用上述标志启动应用程序中的“主”活动。毕竟,如果我“重启”我的应用程序,活动仍在运行,我宁愿保留它的状态信息。

你会注意到在文档中没有提到启动一个新任务:

If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent. For example, consider a task consisting of the activities: A, B, C, D. If D calls startActivity() with an Intent that resolves to the component of activity B, then C and D will be finished and B receive the given Intent, resulting in the stack now being: A, B. The currently running instance of activity B in the above example will either receive the new intent you are starting here in its onNewIntent() method, or be itself finished and restarted with the new intent. If it has declared its launch mode to be "multiple" (the default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be finished and re-created; for all other launch modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to the current instance's onNewIntent(). This launch mode can also be used to good effect in conjunction with FLAG_ACTIVITY_NEW_TASK: if used to start the root activity of a task, it will bring any currently running instance of that task to the foreground, and then clear it to its root state. This is especially useful, for example, when launching an activity from the notification manager.

所以,我的情况如下所示:

A用FLAG_ACTIVITY_CLEAR_TOP启动B, A结束。 B希望重新启动服务,因此将用户发送给具有服务重新启动逻辑的a 和UI(没有标志)。 A以FLAG_ACTIVITY_CLEAR_TOP启动B, A结束。

在此阶段,第二个FLAG_ACTIVITY_CLEAR_TOP标志正在重新启动任务堆栈中的B。我认为这必须破坏任务并开始一个新的任务,导致我的问题,如果你问我,这是一个非常困难的情况!

所以,如果我的假设都是正确的

启动程序只恢复最初创建的任务 FLAG_ACTIVITY_CLEAR_TOP如果重新启动唯一剩下的Activity,也会重新创建一个新的Task

其他回答

您正在经历的行为是由一些Android启动器自API 1以来存在的问题引起的。您可以在这里找到有关该错误的详细信息以及可能的解决方案:https://code.google.com/p/android/issues/detail?id=2373。

在三星设备和其他使用自定义启动器/皮肤的制造商上,这是一个相对常见的问题。我还没见过这个问题发生在普通的Android启动器上。

基本上,应用程序实际上并没有完全重新启动,但是当应用程序被启动器恢复时,您的启动活动正在启动并添加到活动堆栈的顶部。当你恢复应用程序并显示启动活动时,你可以通过单击后退按钮来确认这是情况。然后你应该被带到你期望在恢复应用程序时显示的活动。

为了解决这个问题,我选择的解决方法是检查Intent。CATEGORY_LAUNCHER类别和意图。ACTION_MAIN意图中启动初始Activity的动作。如果这两个标志是存在的,活动不是在任务的根(意味着应用程序已经在运行),然后我调用finish()在初始活动。这个确切的解决方案可能不适合你,但类似的方法应该适用。

以下是我在初始/启动活动的onCreate()中所做的:

    if (!isTaskRoot()
            && getIntent().hasCategory(Intent.CATEGORY_LAUNCHER)
            && getIntent().getAction() != null
            && getIntent().getAction().equals(Intent.ACTION_MAIN)) {

        finish();
        return;
    }

啊哈!(tldr;请参阅底部粗体的语句)

我发现问题了……我认为。

我先从一个假设开始。当你按下启动器时,它会启动默认的Activity,或者,如果之前启动的Task是打开的,它会把它带到前面。换句话说,如果在导航的任何阶段,你创建了一个新的任务,并完成了旧的任务,启动器现在将不再继续你的应用程序。

如果这个假设是正确的,我很确定这应该是一个错误,因为每个任务都在同一个过程中,并且与创建的第一个任务一样是有效的简历候选人。

然后,我的问题通过从几个intent中删除这些标志来解决:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK );

虽然很明显FLAG_ACTIVITY_NEW_TASK创建了一个新Task,但我没有意识到上面的假设是有效的。我确实认为这是一个罪魁祸首,并将其移除进行测试,但我仍然有问题,所以我驳回了它。但是,我仍然具备以下条件:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)

我的启动画面使用上述标志启动应用程序中的“主”活动。毕竟,如果我“重启”我的应用程序,活动仍在运行,我宁愿保留它的状态信息。

你会注意到在文档中没有提到启动一个新任务:

If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent. For example, consider a task consisting of the activities: A, B, C, D. If D calls startActivity() with an Intent that resolves to the component of activity B, then C and D will be finished and B receive the given Intent, resulting in the stack now being: A, B. The currently running instance of activity B in the above example will either receive the new intent you are starting here in its onNewIntent() method, or be itself finished and restarted with the new intent. If it has declared its launch mode to be "multiple" (the default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be finished and re-created; for all other launch modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to the current instance's onNewIntent(). This launch mode can also be used to good effect in conjunction with FLAG_ACTIVITY_NEW_TASK: if used to start the root activity of a task, it will bring any currently running instance of that task to the foreground, and then clear it to its root state. This is especially useful, for example, when launching an activity from the notification manager.

所以,我的情况如下所示:

A用FLAG_ACTIVITY_CLEAR_TOP启动B, A结束。 B希望重新启动服务,因此将用户发送给具有服务重新启动逻辑的a 和UI(没有标志)。 A以FLAG_ACTIVITY_CLEAR_TOP启动B, A结束。

在此阶段,第二个FLAG_ACTIVITY_CLEAR_TOP标志正在重新启动任务堆栈中的B。我认为这必须破坏任务并开始一个新的任务,导致我的问题,如果你问我,这是一个非常困难的情况!

所以,如果我的假设都是正确的

启动程序只恢复最初创建的任务 FLAG_ACTIVITY_CLEAR_TOP如果重新启动唯一剩下的Activity,也会重新创建一个新的Task

这个解决方案对我很有效:

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            Intent startMain = new Intent(Intent.ACTION_MAIN);
            startMain.addCategory(Intent.CATEGORY_HOME);
            startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(startMain);
            return false;
        }
        else
            return super.onKeyUp(keyCode, event);
    }

信贷: 我需要最小化android应用程序的后退按钮点击

可能不能在所有设备上工作,但当返回按钮被按下时,会成功地创建home键行为,从而停止活动而不是结束活动。

这个问题在2016年仍然有意义。今天,一名QA测试人员报告说,我的一个应用程序在Android M中重新启动,而不是从库存启动器恢复。

实际上,系统正在将已启动的活动添加到当前任务堆栈中,但在用户看来,好像发生了重新启动,他们丢失了工作。顺序是:

从play store下载(或旁载apk) 从play store对话框启动应用程序:出现活动A[任务堆栈:A] 导航到活动B[任务堆栈:A -> B] 按“Home”键 从应用程序抽屉中启动应用程序:出现活动A ![任务堆栈:A -> B -> A](用户可以按“后退”键从这里进入活动“B”)

Note: this problem does not manifest for debug APK's deployed via ADB, only in APKs downloaded from the Play Store or side-loaded. In the latter cases, the launch intent from step 5 contained the flag Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT, but not in the debug cases. The problem goes away once the the app has been cold-started from the launcher. My suspicion is the Task is seeded with a malformed (more accurately, non-standard) Intent that prevents the correct launch behavior until the task is cleared entirely.

我尝试了各种活动启动模式,但这些设置偏离了用户期望的标准行为:在活动b中恢复任务。在“开始一个任务”页面底部的“任务和Back Stack”中,可以看到以下预期行为的定义:

这种意图过滤器会在应用程序启动器中显示活动的图标和标签,使用户可以启动活动,并在活动启动后随时返回到它所创建的任务。

我发现这个答案是相关的,并将以下内容插入到我的根活动(A)的“onCreate”方法中,以便在用户打开应用程序时适当地恢复。

                    /**
     * Ensure the application resumes whatever task the user was performing the last time
     * they opened the app from the launcher. It would be preferable to configure this
     * behavior in  AndroidMananifest.xml activity settings, but those settings cause drastic
     * undesirable changes to the way the app opens: singleTask closes ALL other activities
     * in the task every time and alwaysRetainTaskState doesn't cover this case, incredibly.
     *
     * The problem happens when the user first installs and opens the app from
     * the play store or sideloaded apk (not via ADB). On this first run, if the user opens
     * activity B from activity A, presses 'home' and then navigates back to the app via the
     * launcher, they'd expect to see activity B. Instead they're shown activity A.
     *
     * The best solution is to close this activity if it isn't the task root.
     *
     */

    if (!isTaskRoot()) {
        finish();
        return;
    }

UPDATE:将这个解决方案从解析意图标志移到查询活动是否直接位于任务的根。意图标志很难预测和测试所有打开MAIN活动的不同方式(从home启动,从“向上”按钮启动,从Play Store启动等)。

我在三星设备上也遇到了同样的问题。经过一番搜索,这些答案没有一个对我有用。我发现在AndroidManifest.xml文件中,launchMode被设置为singleInstance (android:launchMode="singleInstance")。删除launchMode属性修复了我的问题。