应用程序重启而不是重启


194

希望有人能帮助我找出(如果不是解决方案的话)至少是一种行为的解释。

问题:

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

细节:

当您按下“启动器图标”时,应用程序将正常启动-也就是说,我假设启动了一个Intent,其中包含您的第一个Activity操作android.intent.action.MAIN和类别的名称android.intent.category.LAUNCHER。但是,情况并非总是如此:

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

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

  • 在Motorola Xoom上,当您按启动器图标时,无论当前正在运行什么,该应用程序将始终启动初始启动Activity。我假设启动器图标始终启动“ LAUNCHER”意图。

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

我读过很多回答的声音类似我的问题,但简单地添加android:alwaysRetainTaskState="true"或使用launchMode="singleTop"Activity并不是答案。

编辑:

在此应用程序的最新启动后,我们发现在第一次重新启动后,所有设备上开始出现此现象。这对我来说似乎很疯狂,但是在重新启动过程中,我实际上找不到问题所在。


1
这似乎是一个琐碎的问题,但是您是否在Xoom的开发选项中将“不保留活动”设置为true?
安德鲁·舒斯特

不,(我希望!:))-我已经记录了每个活动的生命周期,并在后台记录了活动的活动状态(它们已停止-未销毁)。操作系统似乎finish()Activity再次启动第一个实例的情况下调用它们,而不是恢复它们。
Graeme 2013年

1
如果您按下主屏幕按钮,然后单击启动器图标,则恢复行为是android的默认行为,您可能已经知道了。但是,如果您按“后退”按钮返回主屏幕,则大多数电话将完成()应用程序。在不同的设备上,退出应用程序所使用的任何方法是否有可能不同?您可以登出onKeyUpEvent来检查其中的某些键不是很奇怪吗?
尼克·卡多佐

2
否-我确信上述问题。使用home将应用程序置于后台(而不是回到后台,您说对了,将完成()Activity)。在Xoom上,可以从任务列表(而不是从启动器)中恢复该应用程序,因此肯定不会杀死Backstack 。
Graeme 2014年

1
悬而未决的答案是解决问题中所述问题的方法。将我自己的答案标记为“正确”,因为尽管有时问题是由启动器中的应用程序错误引起的(如他的回答所述),但我的特定问题却是由任务切换引起的。这两个问题的解决方案都由他的解决方案确定。
Graeme 2014年

Answers:


237

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

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

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

我选择实施以解决此问题的解决方法是检查启动初始Activity的intent中的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;
    }

4
到目前为止,这对我有效,到目前为止,没有任何副作用。基于逻辑假设,我认为没有任何理由无效。
javahead76

3
我认为这是处理此错误的正确方法。为我工作。
索科洛夫

3
为我完成了这项工作,并在大约8种不同的设备上进行了验证。非常感谢!
shaya ajzner

3
WOOOOOW解决了我的问题,而我过去两个小时一直在寻找解决方法
Jean Raymond Daher

2
谢谢@ starkej2。像魅力一样工作。
拉杰夫·萨胡

54

这个问题在2016年仍然存在。今天,一个质量检查人员报告说,该应用可以重启我的应用,而不是 Android M中的启动器中恢复。

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

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

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

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

这种意图过滤器使活动的图标和标签显示在应用程序启动器中,从而为用户提供了一种启动活动并在活动启动后随时返回创建的任务的方法。

我发现此答案很重要,并将以下内容插入了我的根活动(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;
    }

更新:此解决方案从解析意图标志转移到查询活动是否直接在任务的根源。意图标记很难以各种不同的方式来进行MAIN活动(从家中启动,从“向上”按钮启动,从Play商店启动等)进行预测和测试。


4
“一旦启动器对应用进行了冷启动,问题就消失了。” 这是最奇怪的部分,我也观察到了-在首次启动后杀死了该应用程序,它再次开始正常运行。如此奇怪的错误。感谢您对此进行全面分析并提出解决方案。
Oded

11
注意:即使您是通过Android Studio安装了APK,也可以通过从应用程序的Google Play页面使用“打开”初步清除问题(如前所述,通过“冷启动”)重新获得该问题。我发现这对于验证修复程序非常有用。
Oded

很好的解释!
karanatwal.github.io

感谢您的解释:)
AndroidEnthusiast

2
许多应用程序都会发生这种情况。Google相册是我测试过的主要照片。
Raghubansh Mani

19

啊哈!(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传递给(现在顶部)将旧活动作为新的Intent。

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

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

此启动模式还可以与FLAG_ACTIVITY_NEW_TASK结合使用,以达到良好效果:如果用于启动任务的根活动,它将把该任务的任何当前正在运行的实例置于前台,然后将其清除为根状态。例如,当从通知管理器启动活动时,此功能特别有用。

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

  • AB与一起推出FLAG_ACTIVITY_CLEAR_TOPA完成了。
  • B希望重新启动服务,因此将A具有服务重新启动逻辑和UI(无标志)的用户发送给该用户。
  • AB以FLAG_ACTIVITY_CLEAR_TOP 启动,A完成。

在此阶段,第二个FLAG_ACTIVITY_CLEAR_TOP标志正在重新启动B,该标志位于任务堆栈中。我假设这必须销毁Task并启动一个新的,从而导致我的问题,如果您问我,这是非常困难的情况!

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

  • Launcher只恢复最初创建的任务
  • FLAG_ACTIVITY_CLEAR_TOP如果重新启动剩余的剩余部分Activity,还将重新创建一个新的Task

如果FLAG_ACTIVITY_CLEAR_TOP是仅有的剩余活动,则不会创建新任务或重新启动您尝试启动的活动。“如果已设置,并且正在启动的活动已经在当前任务中运行,那么与其启动该活动的新实例,不如关闭该活动之上的所有其他活动,并将此Intent传递给(最重要的是)将旧活动作为新的意图。”
starkej2 2014年

2
我意识到这不是要-而是通过反复试验在我的情况下就是这种情况。删除这些标志可解决此问题。
Graeme 2014年

在所有情况下,这都不能在所有设备上创建完美的简历。
danny117

删除标志为我解决了这个问题。谢谢
Sealer_05 '17

我确实有启动屏幕/主屏幕的情况,但是我没有使用任何标志从启动屏幕过渡到主屏幕,但是问题对我来说还是可以重现的-此解决方案对我不起作用
ror

12

我在三星设备上遇到了同样的问题。经过大量搜索后,这些答案都对我无济于事。我发现在AndroidManifest.xml文件中,launchMode将其设置为singleInstanceandroid:launchMode="singleInstance")。删除launchMode属性解决了我的问题。


确实,这也对我有用。我发现另一个SO问题的答案是有帮助的:stackoverflow.com/a/21622266/293280。而不同类型的这个写了launchMode值:inthecheesefactory.com/blog/...
约书亚品特

此修复程序对我有用!不是在清单中而是在主要活动类的活动属性中添加了此功能。
卡林·弗拉辛

@CalinVlasin您能告诉我您如何使用launchMode吗?你放在哪里?当前,我具有这样的功能,但会导致问题:<activity android:name =“。UI.landing.MyActivity” android:configChanges =“ locale | layoutDirection” android:launchMode =“ singleTop” android:windowSoftInputMode =“ stateAlwaysHidden | adjustResize “>
j2emanue

这也是我的问题。我认为这应该与公认的答案结合使用(!isTaskRoot ...
相信

1

在我的Cat s60上,我在开发人员选项中启用了“不保留活动”,再次禁用此功能后,我可以在不丢失应用程序状态的情况下切换应用程序...


不知道如何,但这在我的设备上是打开的。
realPro

0

此解决方案为我工作:

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

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


有趣的技巧,但不能解决所述问题。对于其他问题/问题非常有用。
Graeme

“后退”按钮具有特定的用途,用户希望“后退”按钮能够执行预期的操作。以任何方式覆盖它都是错误的,并且在我看来,这是非常不专业的。
错误发生

-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(),它在单击时自然会退出应用程序。而且,当按下主屏幕按钮并返回到应用程序时,它不会重新启动。


-2

针对不了解如何在Android手机中编程和遇到此问题的人们的解决方案。这主要是由于android版本的升级(仅基于我的假设)。升级后,所有应用程序都会进行优化以减少电池消耗。但是,这反过来会减慢您的设备速度。

怎么解决

转到设置>>应用程序>>应用程序设置(在屏幕上的任意位置查找设置符号-在不同设备上有所不同)>>电池优化(或类似的优化[此处输入图像说明] [1]上)>> 全部移动应用程序处于“未优化”状态(必须手动进行1比1的操作-在某些手机中可能允许/不允许)。您的启动器应用程序需要“未优化”(在我的情况下为Zen UI启动器-这是我的罪魁祸首-如果有时间,您可以尝试优化/不优化并重新启动其他应用程序)。现在重启手机。(无需重置数据/安全模式或任何麻烦)

立即尝试多任务。:)按下启动器图标现在应该可以恢复当前任务。:)您的设备将变为不用担心电池,它仍然会耗尽。


-8

对您的用户而言是无价的。即使在最近使用的应用程序列表中坐了几周之后,也能获得完美的简历。

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

背景: 主活动中尚未启动任务的应用所使用的内存很容易回收。操作系统只需将原始捆绑包传递给onCreate即可重新启动应用程序。但是,您可以将其添加到原始捆绑包中,onSaveInstanceState因此当您的应用由操作系统重新启动时,您可以还原实例状态,而对于重新启动或恢复应用而言,没有人明智。以经典的地图程序为例。用户移动到地图上的某个位置,然后按Home键。两周后,该地图绘制应用程序以及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。两周后,您必须编写完美的简历。对于复杂的应用程序而言,这不是一件容易的事,但我们在您的身后,在这里为您提供帮助。

祝好运


这不是由于常见的内存问题或默认的“暂停状态”条件。我已经在许多设备上尝试了我的应用程序(有些内存较大,有些内存较低)。
Graeme 2014年

这就是为什么将持久性数据保存在onPause中的原因我已经尝试过让我的应用在每天使用的电话上恢复长达两周的时间,并让我告诉您两周后调用onCreate方法并且os传入了我保存在其中的包中onSaveSessionState,然后我使用捆绑包中的数据使活动完全按照我离开时的样子显示。因此,距离我的回答只有三天了,所以您不可能完成为期两周的测试。摘要:可以随时在后台关闭应用程序。
danny117

@ danny117我认为您没有完全了解Graeme遇到的问题
starkej2 2014年

我了解Graeme的问题。在某些设备上,恢复应用程序会调用onCreate。听起来完全像我的HTC EVO(姜饼),它将杀死应用程序,只是旋转屏幕。查看它说的文档,以便可以在onCreate developer.android.com/reference/android/app
danny117

@ danny117是正确的,但是他遇到的问题与恢复应用程序(预期)时重新创建单个活动无关,但正在启动错误的活动
starkej2 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.