希望有人能帮我弄清楚,如果不是解决方案,至少是一个行为的解释。
存在的问题:
在某些设备上,按下启动器图标会导致当前任务被恢复,在其他设备上则会导致初始启动意图被触发(有效地重新启动应用程序)。为什么会发生这种情况?
细节:
当你按下“启动器图标”时,应用程序正常启动——也就是说,我假设,一个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"的活动不是答案。
编辑:
在这个应用程序最近一次启动后,我们发现在第一次重启后,这种行为已经开始在所有设备上发生。这对我来说似乎很疯狂,但通过重新启动过程,我实际上找不到哪里出了问题。
这个问题在2016年仍然有意义。今天,一名QA测试人员报告说,我的一个应用程序在Android M中重新启动,而不是从库存启动器恢复。
实际上,系统正在将已启动的活动添加到当前任务堆栈中,但在用户看来,好像发生了重新启动,他们丢失了工作。顺序是:
从play store下载(或旁载apk)
从play store对话框启动应用程序:出现活动A[任务堆栈:A]
导航到活动B[任务堆栈:A -> B]
按“Home”键
从应用程序抽屉中启动应用程序:出现活动A 
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启动等)。
您正在经历的行为是由一些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;
}
对你的用户来说是无价的。完美的简历,即使在最近使用的应用程序列表中坐了几个星期。
对用户来说,它看起来像一份简历,但实际上是一个全面的开始。
Background:
The memory used by apps that are in the main activity that haven't started a task is easy to reclaim. The os can simply restart the app with the original bundle passed to onCreate. You can however add to the original bundle in onSaveInstanceState so when your app is restarted by the OS you can restore the instance state and no one is the wiser on whether the app restarted or resumed. Take for example the classic map program. The user moves to a position on the map and then presses the home key. Two weeks later this mapping app is still in the list of recent apps along with facebook, pandora, and candy crush. The OS doesn't just save the name of the app for the recently used apps it also saves the original bundle used to start the app. However the programmer has coded the onSaveInstanceState method so the orignal bundle now contains all the materials and information necessary to construct the app so it looks like it was resumed.
例子:
保存当前相机的位置在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那样活几个星期。你必须在两周后写出一份完美的简历。对于复杂的应用程序来说,这不是一项简单的任务,但我们支持你,并在这里提供帮助。
祝你好运