219

希望有人可以帮助我弄清楚,如果不是解决方案,至少是对行为的解释。

问题:

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

细节:

当您按下“启动器图标”时,应用程序会正常启动 - 也就是说,我假设会启动一个 Intent,其中包含您的第一个名称以及Activityactionandroid.intent.action.MAIN和 category android.intent.category.LAUNCHER。然而,情况并非总是如此:

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

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

  • 在摩托罗拉 Xoom 上,当您按下启动器图标时,应用程序将始终启动初始启动Activity,无论当前正在运行什么。我假设启动器图标总是启动“启动器”意图。

  • 在三星 Tab 2 上,当您按下启动器图标时,如果您刚刚安装了该应用程序,它将始终启动初始Activity(与 Xoom 相同) - 但是,在安装后重新启动设备后,启动器图标将改为恢复应用程序。我假设这些设备在设备启动时将“已安装的应用程序”添加到查找表中,从而允许启动器图标正确恢复正在运行的任务?

我已经阅读了许多听起来与我的问题相似的答案,但只是简单地添加android:alwaysRetainTaskState="true"或使用launchMode="singleTop"不是Activity答案。

编辑:

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

4

9 回答 9

269

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

这是三星设备以及使用自定义启动器/皮肤的其他制造商的一个相对常见的问题。我还没有看到股票 Android 启动器上出现此问题。

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

我选择实施以解决此问题的解决方法是检查启动初始活动的意图中的 Intent.CATEGORY_LAUNCHER 类别和 Intent.ACTION_MAIN 操作。如果存在这两个标志并且 Activity 不在任务的根目录(意味着应用程序已经在运行),那么我在初始 Activity 上调用 finish()。该确切的解决方案可能对您不起作用,但应该有类似的解决方案。

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

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

        finish();
        return;
    }
于 2014-04-22T12:51:16.070 回答
64

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

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

  1. 从 Play 商店下载(或侧载 apk)
  2. 从 Play 商店对话框启动应用程序:出现活动 A [任务堆栈:A]
  3. 导航到活动 B [任务堆栈:A -> B]
  4. 按“主页”按钮
  5. 从应用程序抽屉启动应用程序:出现活动 A![任务堆栈:A -> B -> A](用户可以按“返回”按钮从此处进入活动“B”)

注意:此问题不会出现在通过 ADB 部署的调试 APK 中,仅在从 Play 商店下载或侧面加载的 APK 中出现。在后一种情况下,第 5 步的启动意图包含 flag Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT,但在调试情况下不包含。一旦应用程序从启动器冷启动,问题就会消失。我怀疑任务是用格式错误(更准确地说,非标准)的 Intent 播种的,该 Intent 会阻止正确的启动行为,直到任务被完全清除。

我尝试了各种活动启动模式,但这些设置与用户期望的标准行为相差太大:在活动 B 处恢复任务。请参阅页面底部的任务和返回堆栈指南中的预期行为定义在“开始任务”下:

这种意图过滤器会导致 Activity 的图标和标签显示在应用程序启动器中,为用户提供一种启动 Activity 并在启动后随时返回到它创建的任务的方法。

我发现这个答案是相关的,并将以下内容插入到我的根活动 (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;
    }

更新:将此解决方案从解析意图标志转移到查询活动是否直接位于任务的根目录。意图标志很难预测和测试,有各种打开主要活动的方式(从家里启动、从“向上”按钮启动、从 Play 商店启动等)

于 2016-07-12T20:01:46.203 回答
21

啊哈!(tldr;见底部粗体的陈述)

我发现了问题……我想。

所以,我将从一个假设开始。当您按下启动器时,它会启动默认设置Activity,或者,如果Task先前启动的启动已打开,则会将其置于最前面。换一种说法 - 如果在导航的任何阶段您创建了一个新的Taskfinish旧的,启动器现在将不再恢复您的应用程序。

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

然后,我的问题是通过从以下几个中删除这些标志来解决的Intents

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

虽然很明显FLAG_ACTIVITY_NEW_TASK创建了一个 new Task,但我不明白上述假设是否有效。我确实认为这是罪魁祸首并将其删除以进行测试,但我仍然遇到问题,因此我将其驳回。但是,我仍然有以下条件:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)

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

您会在文档中注意到它没有提到开始一个新的Task

如果设置,并且正在启动的活动已经在当前任务中运行,那么不会启动该活动的新实例,而是关闭它上面的所有其他活动,并且此 Intent 将被传递到(现在顶部)作为新意图的旧活动。

例如,考虑一个由以下活动组成的任务:A、B、C、D。如果 D 调用 startActivity() 并带有一个解析为活动 B 的组件的 Intent,那么 C 和 D 将完成并且 B 接收给定的 Intent ,导致堆栈现在是:A,B。

上面示例中当前运行的活动 B 实例将在其 onNewIntent() 方法中接收您在此处启动的新意图,或者自身完成并使用新意图重新启动。如果它已声明其启动模式为“多重”(默认)并且您没有在同一意图中设置 FLAG_ACTIVITY_SINGLE_TOP,那么它将完成并重新创建;对于所有其他启动模式,或者如果设置了 FLAG_ACTIVITY_SINGLE_TOP,则此 Intent 将被传递到当前实例的 onNewIntent()。

这种启动模式也可以与 FLAG_ACTIVITY_NEW_TASK 结合使用,效果很好:如果用于启动任务的根活动,它将将该任务的任何当前正在运行的实例带到前台,然后将其清除为根状态。这尤其有用,例如,当从通知管理器启动活动时。

因此,我遇到了如下所述的情况:

  • A启动BFLAG_ACTIVITY_CLEAR_TOP,A完成。
  • B希望重新启动服务,因此将A具有服务重新启动逻辑和 UI(无标志)的用户发送到该用户。
  • A以 FLAG_ACTIVITY_CLEAR_TOP启动BA完成。

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

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

  • Launcher唯一恢复最初创建的任务
  • FLAG_ACTIVITY_CLEAR_TOP将,如果它重新启动唯一剩下的Activity,也会重新创建一个新的Task
于 2014-04-17T12:47:43.700 回答
16

我在三星设备上遇到了同样的问题。经过大量搜索,这些答案都不适合我。我发现在AndroidManifest.xml文件中,launchMode设置为singleInstance( android:launchMode="singleInstance")。删除该launchMode属性解决了我的问题。

于 2017-04-11T11:50:19.850 回答
4

在我的 Cat s60 上,我在开发者选项中启用了“不保留活动”,再次禁用它可以让我切换应用程序而不会丢失应用程序的状态......

于 2018-11-27T20:37:28.280 回答
1

这个解决方案对我有用:

    @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 应用程序

可能不适用于所有设备,但在按下后退按钮时成功创建主页按钮行为,从而停止活动而不是完成活动。

于 2017-11-22T21:51:17.100 回答
-1

我遇到了同样的问题,原因是:

(Kotlin 代码,在 MainActivity 中)

override fun onBackPressed() {
    finish()
}

所以当从我的 LoginActivity 导航到我的 MainActivity 时,我使用这个:

    val intent = Intent(this, MainActivity::class.java)
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    startActivity(intent)

使用这些标志时,我的 MainActivity 中不必有onBackPressed(),它会在单击后自然退出应用程序。当按下主页按钮并返回应用程序时,它不会重新启动。

于 2018-09-13T05:42:34.987 回答
-3

为不知道在他们的 Android 手机中编程和遇到此问题的人提供的解决方案。这主要是由于android版本的升级(只是我的假设)。升级后,您的所有应用程序都经过优化以使用更少的电池。但是,这反过来会减慢您的设备速度。

怎么解决

转到设置>>应用程序>>应用程序设置(在屏幕上的任何位置查找设置标志-在不同设备上有所不同)>>电池优化(或类似的选项[在此处输入图像描述][1]打开)>>全部移动应用程序处于“未优化”状态(必须手动逐一进行 - 在某些手机中可能是允许/禁止)。您的启动器应用程序需要“未优化”(在我的情况下是 Zen UI 启动器 - 我猜这是罪魁祸首 - 如果有时间,您可以尝试优化/不优化和重新启动不同的应用程序)。现在重启你的手机。(无需重置数据/安全模式或任何麻烦)

立即尝试多任务处理。:) 现在按下启动器图标应该会导致当前任务被恢复。:) 你的设备会变成不用担心电池,无论如何它都会耗尽。

于 2018-06-28T10:33:55.787 回答
-8

对您的用户来说是无价的。即使在最近使用的应用程序列表中呆了数周后,它仍然是一份完美的简历。

对用户来说,它看起来像是一份简历,但实际上是一个完整的开始。

背景: 主要活动中尚未开始任务的应用程序使用的内存很容易回收。操作系统可以简单地使用传递给 onCreate 的原始包重新启动应用程序。但是,您可以添加到原始捆绑包中,onSaveInstanceState以便当您的应用程序被操作系统重新启动时,您可以恢复实例状态,并且没有人知道应用程序是重新启动还是恢复。以经典的地图程序为例。用户移动到地图上的一个位置,然后按下主页键。两周后,这个地图应用程序仍然与 facebook、pandora 和 candy crush 一起出现在最近的应用程序列表中。操作系统不仅为最近使用的应用程序保存应用程序的名称,它还保存用于启动应用程序的原始包。然而,程序员已经编码onSaveInstanceState方法,因此原始包现在包含构建应用程序所需的所有材料和信息,因此看起来它已恢复。

示例: 将当前相机位置保存在 onSaveInstanceState 中,以防应用程序被卸载并且必须在几周后从最近的应用程序列表中重新启动。

@Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        // save the current camera position;
        if (mMap != null) {
            savedInstanceState.putParcelable(CAMERA_POSITION,
                    mMap.getCameraPosition());
        }
    }



@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // get the exact camera position if the app was unloaded.
        if (savedInstanceState != null) {
            // get the current camera position;
            currentCameraPosition = savedInstanceState
                    .getParcelable(CAMERA_POSITION);
        }

注意:您也可以使用该onRestoreInstanceState方法,但我发现在onCreate.

这很可能是您的应用程序中发生的事情。在某些设备上,您的应用会被卸载以释放内存。是的,有一些标志会有所帮助,但这些标志不会捕捉到您应用程序的每一个细微差别,而且这些标志不会像您那样让您在数周内保持活力onSaveInstanceState。你必须在两周后编写一份完美的简历。对于复杂的应用程序来说,这不是一件容易的事,但我们在您身后并随时为您提供帮助。

祝你好运

于 2014-04-20T05:16:51.760 回答