为什么具有launchMode="singleTask"的启动器活动始终会被推到返回栈的顶部,即使另一个活动处于顶部?

8

我的启动器活动由于某些要求,设置了launchMode属性为singleTask。

<activity
    android:name=".map.MapsActivity"
    android:launchMode="singleTask"
    android:screenOrientation="portrait"
    android:theme="@style/MapScreenTheme"
    android:windowSoftInputMode="adjustPan">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

我所面临的问题是:如果我打开另一个活动 -> 按下主页键 -> 在启动器应用程序中单击应用程序图标 -> 它会打开地图活动而不是之前打开的活动。
但是,如果我通过最近使用菜单导航到应用程序,则新打开的活动会保持在顶部。
请问有人能解释一下关于后退堆栈(backstack)正在发生什么,并且为什么 ActivityManagerService 没有考虑到应用程序进程已经存在并决定启动启动器应用程序并清除后退堆栈,而不是简单地将应用程序置于最前面?
可以在此处创建的小示例应用程序中观察到此问题 - https://github.com/abhiank/SingleTaskActivity

2
你尝试过从这个帖子中的任何解决方案吗? - ADM
@ADM。非常感谢您。我简直不敢相信我尽管搜索了很多次,但仍然没有找到这个线程。因此,最终的解决方案是拥有一个帮助器活动来启动主活动,因为singleTask会杀死其上面的所有其他活动。 - vepzfe
1
是的。我想我应该深入挖掘一下。嗯,那不是真正的解决方案。只是一个hacky的解决方法。我认为最好的方法是将此线程标记为重复,并引用该线程。 - vepzfe
一般来说,使用特殊的“launchMode”会引起更多问题而不是解决问题。请解释一下为什么您认为需要这种特殊的启动模式。 - David Wasser
@DavidWasser 我正在使用 taskAffinity。这就是为什么 URL 共享活动不会影响主要后退堆栈的原因。我也很想在主要后退堆栈上使用 taskAffinity,但问题在于我无法在意图中设置自定义 taskAffinity 以从 URL 活动打开主要活动。 - vepzfe
显示剩余8条评论
4个回答

3

这不是正确的。singleTask 的期望行为在此处描述:https://developer.android.com/guide/components/activities/tasks-and-back-stack#ManifestForTasks 如果您查看“图4”,它甚至明确显示了一个包含2个活动的任务,其中根 Activity 声明为 launchMode="singleTask" - David Wasser
感谢 @DavidWasser,我会阅读的。 - ADM
1
实际上,我刚刚发现了一个非常详细的答案,回答了一个相关问题,涵盖了很多这个主题(尽管没有特别涉及launchMode="singleTask"的行为)。请看这里:https://dev59.com/qF0b5IYBdhLWcg3wUP4X#29376250 - David Wasser

2
当你再次点击应用程序图标时,它会在之前打开的活动的顶部打开启动器活动。你可以做的是检查除此之外是否有任何活动在后台,然后结束这个启动器活动,它将显示之前打开的屏幕。请尝试下面的链接。 https://dev59.com/mJnga4cB1Zd3GeqPSw2i#38450232 还要检查这些链接,它们有其他方法来维护启动应用程序图标时的屏幕,当应用程序在后台运行时。 如何使Android应用程序在重新启动时返回到上次打开的活动? Android应用程序在通过单击应用程序图标打开时重新启动 确定单击应用程序图标以在Android中启动应用程序的时间

从“最近使用的应用程序”启动应用程序和点击应用程序图标有什么区别?


谢谢你的回答。我尝试了你链接中提到的答案,但是由于带有“singleTask”的活动在后台堆栈中,onCreate从未被调用。我尝试将相同的代码放入onNewIntent()中,它被调用了,但是此时isTaskRoot()已经为true。这意味着后台堆栈已经清除,并且在调用onNewIntent之前,该活动已经被放置在顶部。再次感谢你提供的链接。那个问题也遇到了我正在面对的同样的问题。 - vepzfe
@abhiank更新了答案,并提供了更多链接,其中包含其他方法,可能会对您有所帮助,请仔细阅读。 - akashzincle
再次感谢那些链接。很有趣的阅读。但是,它们中没有一个是有用的。其中大部分都涉及到从安装程序“打开”按钮与启动器图标单击打开应用程序的问题。我希望你最后一个链接中有一些关于从最近列表恢复应用程序时发生了什么不同的深入工作,但遗憾的是,它没有提供所需的信息。 - vepzfe

0

听起来你在遇到这个长期存在的令人讨厌的 Android Bug。

重新启动 Activity 的 Home 按钮,但......只有第一次

为了测试这是否是情况,请关闭你的应用程序(设置->应用程序->你的应用程序->强制退出)。然后通过在主屏幕上点击应用程序图标来启动你的应用程序。做任何你需要做的事情来启动另一个Activity到任务中。按 HOME 键。现在再次在主屏幕上点击该图标。这应该将你的任务带到前台,处于你离开它时的相同状态。


嘿@David,感谢你的回答。然而,这不是我面临的错误。实际上,错误是这个 - https://dev59.com/v3E95IYBdhLWcg3wMa91 - vepzfe
相关问题描述了基本相同的错误。那个问题的答案有些是完全错误的,但如果存在错误,那就是我所描述的错误。您是否按照我的建议进行了测试? - David Wasser
我确实尝试过了。这不是正在发生的情况。这些是不同的错误。在我的情况下,主活动的onCreate没有被调用。首先Activity B的onDestroy被调用,然后是onDetachedFromWindow,最后是mainActivity的onNewIntent。而且在newIntent中,isTaskRoot()总是为true。实际上,在这一点上,我认为这不能被称为一个错误。似乎singleTask的活动会表现出这种方式。唯一的解决方案是拥有一个委托活动。 - vepzfe

0

如果您的主要活动仅由您自己应用程序中的其他活动启动,则有一些解决方案:

您可以从清单中删除主要活动定义中的launchMode属性。并且只需将Intent.FLAG_ACTIVITY_NEW_TASK传递给打开MainActivity的每个意图,例如:

Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

请注意,在您自己的应用程序中,Intent.FLAG_ACTIVITY_NEW_TASKlaunchMode="singleTask"的行为仅与清单文件中活动定义的taskAffinity属性配合使用才能正常工作。
希望这能对您有所帮助。

Intent.FLAG_ACTIVITY_NEW_TASK 和 launchMode="singleTask" 具有相同的行为。我的建议是通过意图标志设置 "singleTask" 模式,而不是属性。当然,如果您的应用程序将被其他应用程序使用(向其发送意图),则我的解决方案对您无效,否则您将完全控制对主活动的意图。至于来自启动器应用程序的意图,没有必要控制它,使用这种方法可以很好地工作。 - Николай Гольцев
你是在说我使用一个常见的活动,即启动器活动,并使用它来启动我的主要活动吗? - vepzfe
很抱歉,我仍然不明白如果从主屏幕打开活动,我应该如何设置flag = Intent.FLAG_ACTIVITY_NEW_TASK。那部分由ActivityManager控制,我无法在那里设置标志! - vepzfe
好的,我明白了。您不需要处理这个特定的情况(从主屏幕更改意图),如果您只是将其保持原样,它就能正常工作。 - Николай Гольцев
你的解决方案仍然不起作用。我在问题中明确提到的问题是HOME->A->B->HOME,然后点击应用程序图标会打开A而不是A->B。我从未提到过A是从任何其他活动中打开的。 - vepzfe
显示剩余5条评论

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