Android默认启动模式下的LAUNCHER活动是什么?

18

launchMode属性在manifest中的launcher activity是否被忽略了? 根据android文档,其默认值为"standard",但如果将其应用于应用程序的主要活动,则对我来说这似乎是不合理的,因为每次启动应用程序时,都会在实例中创建另一个task


这个问题被很好地阐述了,仍需要一个答案。例如,如果您有一个简单的测试应用程序,省略了“launchMode”,这意味着“标准”,那么在启动器中点击此应用程序时,每次都应该启动新任务。但是这并不是事实(至少在Android 6.0.1中)。如果应用程序已经在运行,则启动器会将旧实例带到前台。 - Stan
5个回答

10

我自己深入研究了Android源代码,并发现了以下内容。

启动器使用LauncherAppsService中的startActivityAsUser方法启动应用程序。该意图是使用以下行构建的:

Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
launchIntent.setComponent(component);
launchIntent.setSourceBounds(sourceBounds);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
根据Android文档,标志FLAG_ACTIVITY_NEW_TASK的含义是:
当使用此标志时,如果已经为您现在要启动的活动运行了一个任务,则不会启动新的活动;取而代之的是,当前任务将带有其最后一次状态简单地带到屏幕前面。
这实际上无条件地覆盖了应用程序中指定的launchMode(或默认行为省略该属性),并忽略了此属性。
我认为这证明了文档不够清晰(或完整)。没有进行核心源代码的深入研究,每个人都可能偶尔会得到意外结果。

似乎FLAG_ACTIVITY_NEW_TASK的命名有些具有欺骗性。 - Kevin Krumwiede

8
你混淆了两件事情,一个是launchMode,另一个是“当用户从主屏幕选择应用程序图标或从最近任务列表中选择任务时会发生什么”。这些是完全不同的两种情况。 launchMode 每个Activity都有指定的launchMode(默认为"standard""multiple")。这告诉Android如何启动此Activity,许多因素可以影响launchMode的“解释”。它取决于在使用的Intent中可能已经指定的其他标志。它取决于请求启动Activity的任务(或者如果从非活动上下文,如ServiceBroadcastReceiver中请求启动,则取决于是哪个任务)。它还取决于指定任务中是否已经存在Activity实例等。
从主屏幕或已安装应用程序列表中选择应用程序图标时的行为
当用户选择应用程序图标时,将调用startActivity()并包含以下数据的Intent
  • ACTION=MAIN
  • CATEGORY=LAUNCHER
  • 组件设置为在清单文件中定义了ACTION=MAIN和CATEGORY=LAUNCHER的Activity的包名和类名
  • 设置标志FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_RESET_TASK_IF_NEEDED
无论要启动的ActivitylaunchMode定义如何,使用这样的Intent调用startActivity()都会导致以下行为:
如果已经存在一个任务的任务亲和力与将要启动的Activity匹配(简单地说,如果应用程序已经在运行),Android将简单地将现有任务带到前台。仅此而已。它不创建任何Activity实例。它不在任何Activity上调用onNewIntent()。除了将现有任务带到前台之外,它什么都不做。这就是为什么,即使您的启动器Activity指定了launchMode="standard",每次单击应用程序图标时Android也不会创建新的实例的原因。
如果尚不存在与要启动的Activity具有任务亲和性的现有任务(简单地说,如果应用程序尚未运行),则Android会创建一个新任务并将Activity启动到该任务中。由于在将单个Activity启动到新任务时,无论启动模式如何都没有任何区别,因此launchMode在这里不起作用。Android始终创建新任务,并始终创建新Activity的实例作为该任务的根。
当用户从最近任务列表中选择任务时,此行为也相同。如果任务仍在运行,则Android只将任务带到前台,不会启动任何新Activity实例,并且不会调用onNewIntent()。如果任务不再运行,则Android创建一个新任务,并将launcher Activity启动到该任务中。唯一的区别是,如果用户从最近任务列表中选择了任务,则Intent中还设置了FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY标志。
我希望这回答了您的问题。
有关FLAG_ACTIVITY_RESET_TASK_IF_NEEDED和任务重新分配的非常详细的解释,请参见此答案

谢谢你的回答。我不认为我混淆了两件事情。我的和你的回答基本上意思相同:launchMode在许多情况下都不起作用,并且被“外部”意图覆盖,我认为这应该在launchMode文档中更详细地描述。 - Stan
听起来像是问题的原始发布者混淆了两件事情。此外,许多东西都有依赖关系。在Android中,几乎所有的标志、设置、条件等都必须在特定的上下文中考虑。总的来说,我同意,文档可以改进。然而,如果所有的“如果、但是和何时”的描述都在文档中,那么阅读起来会变得非常混乱,大多数新开发人员只会去为iOS编写代码。 - David Wasser
再次参考原帖:Android在用户再次启动应用程序时不创建 Activity 的另一个实例,这并没有任何与launchMode有关。无论指定什么launchMode都不会有任何区别。这种行为根本与launchMode无关。它也与主窗口背景颜色,使用的字体或月相无关。我不明白为什么需要在文档中指定这一点。 - David Wasser
你最后的评论听起来好像launchMode是无用或者误导性的。这确实是“关于Activity应该如何启动的指示”,但并不代表它一定会以这种方式被启动。 - Stan
我并不是说launchMode是无用或者误导性的。我是在说,还有其他因素可以修改launchMode所指定的行为。任务亲和性、启动上下文和Intent标志都可能会影响Android在调用startActivity()时实际执行的操作。 - David Wasser
显示剩余2条评论

2
将以下英文句子翻译成中文:

把开头的活动以外的所有内容都看作是一个抽象实现。将一个活动声明为

        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

会使其首先打开。在形成导航活动的意图时,后续活动将被覆盖。这些覆盖表示为意图标志。

意图附加项列表: http://developer.android.com/reference/android/content/Intent.html

标志是您本来应在清单中编写的命令。


“抽象实现”的类比很奇怪也是错误的。这只会让本来就令人困惑的问题更加混乱。这些答案是如何得到赞同的? - David Wasser
无论您在清单中声明的后续活动上放置了什么意图过滤器,当您在代码中声明要转到这些活动的意图时,它们都会被覆盖。基本上与您拥有抽象类或扩展具有要更改的方法的类相同。除非有对super的调用,否则原始意图不再发生。 - KoalaKoalified
抱歉,那个语句完全没有意义。你又把事情搞混了。<intent-filter>不会被覆盖。当使用隐式Intent启动活动时,才会使用Intent过滤器。如果您使用显式Intent启动Activity,则不会使用Intent过滤器。这与抽象、继承和覆盖无关。 - David Wasser

0

你是对的。默认模式是“标准模式”。

根据Android文档

*在“标准模式”下,每次有新的意图要启动一个“标准”活动时,都会创建该类的新实例来响应该意图。每个实例处理一个意图。

*如果父活动具有启动模式“标准”(并且向上意图不包含FLAG_ACTIVITY_CLEAR_TOP),则当前活动及其父活动都将从堆栈中弹出,并创建父活动的新实例以接收导航意图。


请参考@Stan的回答:当从启动器启动一个“Activity”时,启动模式会被覆盖。 - Marc Plano-Lesay

0

将Activity的行为设置为标准模式,这意味着每次发送Intent时都会创建一个新的Activity以独立地处理该Intent。例如,如果要发送10个Intent来撰写一封电子邮件,就应该启动10个Activity来分别处理每个Intent。因此,在设备上可能会无限制地启动此类Activity。

Android Lollipop之前的行为

在相同的任务中,标准Activity将被创建并置于堆栈的顶部,与发送Intent的Activity相同。例如,当我们从图库分享图像到标准Activity时,尽管它们来自不同的应用程序,但它们将被堆叠在相同的任务中描述。如果我们切换到另一个应用程序,然后再切换回图库,我们仍将看到标准的launchMode置于Gallery的任务顶部。因此,如果我们需要在Gallery中执行任何操作,我们必须先完成该附加Activity中的工作。

enter image description here

Android Lollipop上的行为

如果活动来自同一应用程序,则其行为与Lollipop之前的版本相同,堆叠在任务的顶部。 但是,如果从不同的应用程序发送意图,则会创建新任务,并将新创建的活动放置为根活动,如下所示。

enter image description here

来源于这里


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