了解 Android 带有启动画面的启动模式
Understanding Android Launch modes with splash screens
我有 3 个活动:Splash(启动器)、登录、仪表板。当应用程序打开时,Splash 会等待一段时间然后开始登录,finish
ing 本身。然后用户可以从登录导航到仪表板。
我想在用户单击主页按钮或通过可浏览的 Intent 指向应用程序时维护应用程序的单个实例。以便用户返回到切换应用程序之前的位置。
以下是android:launchMode="standard"
和singleInstancePerTask
的行为(红色方块是完成的活动)
singleTask
的行为类似于标准模式的附加 Web 意图,我可以向 Splash 添加逻辑,以便它中断处理并在它不是 root 时完成:
最后,如果我选择 singleInstance
,会发生以下情况:
这似乎可行,但有些事情我不明白。 为什么第二个 Splash 忽略启动登录的 Intent activity?。逻辑告诉我应该启动登录 #2。
物质表现:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.testintentflags">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.TestIntentFLags">
<activity
android:name=".SplashActivity"
android:exported="true" android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="demo.intents.com"
android:scheme="https" />
</intent-filter>
</activity>
<activity
android:name=".DashboardActivity"
android:exported="false" />
<activity
android:name=".LoginActivity"
android:exported="false" />
</application>
</manifest>
这里发生了很多事情,我会尝试解决这些问题:
首先,在这里使用 launchMode="singleInstance"
是有问题的,因为您还没有为您的任何活动声明任何 taskAffinity
。所以你的 SplashActivity
和你的其他活动有相同的(默认)taskAffinity
这告诉 Android 所有这些活动都想 运行 在同一个任务中,这与你的启动冲突SplashActivity
的模式声明。这很糟糕,你不应该这样做。如果你真的想在单独的任务中有活动运行,你应该确保它们有不同的taskAffinity
,以免造成混淆。
其次,您写道:
This seems to work, but there is something that I don't understand.
Why is the second Splash ignoring the Intent to launch Login
activity?. The logic tells me that a Login #2 should've launched.
这里发生的事情是这样的:当 SplashActivity
调用 startActivity()
启动 LoginActivity
时,使用的 Intent
包含 FLAG_ACTIVITY_NEW_TASK
集合(因为 SplashActivity
是用 launchMode="singleInstance"
声明的,因此它必须将其他活动启动到新任务中),因此 Android 查找以 LoginActivity
作为其根的现有任务 Activity
.如果找不到,它会简单地启动 LoginActivity
的新实例到新任务中。但是,在这种情况下,它会找到一个(这是图表中的任务 #2),因此它只是将该任务带到前台 而不会启动 LoginActivity
[=66 的新实例=].
这与当您有一个应用 运行 时发生的行为相同,然后按下主屏幕按钮,然后再次单击该应用的应用图标。在这种情况下,Android 不会启动应用根 Activity
的新实例,它只是查找以应用启动 Activity
作为根 Activity
的现有任务并将该任务以任何状态置于前台。
我给你的建议如下:
- 摆脱使用特殊启动模式
singleInstance
和 singleTask
- 创建一个新的
BridgeActivity
,用作网络浏览器和您的应用程序之间的桥梁。此 Activity
应该具有浏览器 Intent
的 <intent-filter>
。
BridgeActivity
应该检查它是否是其任务中的根 Activity
。如果不是,这意味着它已经启动到一个已被带到前台的现有任务中,它可以在 onCreate()
. 中安静地 finish()
- 如果
BridgeActivity
检测到它是其任务的根,它可以启动 SplashActivity
(确保设置 FLAG_ACTIVITY_NEW_TASK
)以开始一个新任务(因为显然没有'已经是您应用程序的现有任务)
应该声明 BridgeActivity
,这样它就不会出现在任务堆栈历史记录或最近任务列表中(noHistory="true"
和 excludeFromRecents="true"
)
我有 3 个活动:Splash(启动器)、登录、仪表板。当应用程序打开时,Splash 会等待一段时间然后开始登录,finish
ing 本身。然后用户可以从登录导航到仪表板。
我想在用户单击主页按钮或通过可浏览的 Intent 指向应用程序时维护应用程序的单个实例。以便用户返回到切换应用程序之前的位置。
以下是android:launchMode="standard"
和singleInstancePerTask
的行为(红色方块是完成的活动)
singleTask
的行为类似于标准模式的附加 Web 意图,我可以向 Splash 添加逻辑,以便它中断处理并在它不是 root 时完成:
最后,如果我选择 singleInstance
,会发生以下情况:
物质表现:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.testintentflags">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.TestIntentFLags">
<activity
android:name=".SplashActivity"
android:exported="true" android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="demo.intents.com"
android:scheme="https" />
</intent-filter>
</activity>
<activity
android:name=".DashboardActivity"
android:exported="false" />
<activity
android:name=".LoginActivity"
android:exported="false" />
</application>
</manifest>
这里发生了很多事情,我会尝试解决这些问题:
首先,在这里使用 launchMode="singleInstance"
是有问题的,因为您还没有为您的任何活动声明任何 taskAffinity
。所以你的 SplashActivity
和你的其他活动有相同的(默认)taskAffinity
这告诉 Android 所有这些活动都想 运行 在同一个任务中,这与你的启动冲突SplashActivity
的模式声明。这很糟糕,你不应该这样做。如果你真的想在单独的任务中有活动运行,你应该确保它们有不同的taskAffinity
,以免造成混淆。
其次,您写道:
This seems to work, but there is something that I don't understand. Why is the second Splash ignoring the Intent to launch Login activity?. The logic tells me that a Login #2 should've launched.
这里发生的事情是这样的:当 SplashActivity
调用 startActivity()
启动 LoginActivity
时,使用的 Intent
包含 FLAG_ACTIVITY_NEW_TASK
集合(因为 SplashActivity
是用 launchMode="singleInstance"
声明的,因此它必须将其他活动启动到新任务中),因此 Android 查找以 LoginActivity
作为其根的现有任务 Activity
.如果找不到,它会简单地启动 LoginActivity
的新实例到新任务中。但是,在这种情况下,它会找到一个(这是图表中的任务 #2),因此它只是将该任务带到前台 而不会启动 LoginActivity
[=66 的新实例=].
这与当您有一个应用 运行 时发生的行为相同,然后按下主屏幕按钮,然后再次单击该应用的应用图标。在这种情况下,Android 不会启动应用根 Activity
的新实例,它只是查找以应用启动 Activity
作为根 Activity
的现有任务并将该任务以任何状态置于前台。
我给你的建议如下:
- 摆脱使用特殊启动模式
singleInstance
和singleTask
- 创建一个新的
BridgeActivity
,用作网络浏览器和您的应用程序之间的桥梁。此Activity
应该具有浏览器Intent
的<intent-filter>
。 BridgeActivity
应该检查它是否是其任务中的根Activity
。如果不是,这意味着它已经启动到一个已被带到前台的现有任务中,它可以在onCreate()
. 中安静地 - 如果
BridgeActivity
检测到它是其任务的根,它可以启动SplashActivity
(确保设置FLAG_ACTIVITY_NEW_TASK
)以开始一个新任务(因为显然没有'已经是您应用程序的现有任务)
应该声明 BridgeActivity
,这样它就不会出现在任务堆栈历史记录或最近任务列表中(noHistory="true"
和excludeFromRecents="true"
)
finish()