从历史堆栈中移除一个活动

402

我的应用程序在用户第一次运行应用时显示一个注册活动,如下所示:

  1. ActivitySplashScreen(欢迎来到游戏,注册一个帐户?)
  2. ActivitySplashScreenSignUp(很好,请填写此信息)
  3. ActivityGameMain(主游戏屏幕)

因此,当用户通过每个屏幕上的按钮点击时,活动依照这个顺序精确启动。

当用户从第2个活动转到第3个活动时,是否有可能完全清除历史堆栈中的第1和第2个活动?我希望如果用户正在第3个活动中,并单击返回按钮,则只需返回到主屏幕,而不是返回到闪屏屏幕。

我认为可以使用任务来实现此目的(即在第3步开始新任务),但想知道是否有更简单的方法,谢谢。


当用户从主屏幕恢复您的应用程序时,它会将他带到ActivitySplashScreen还是ActivityGameMain? - Tamil
16个回答

668

您可以通过在AndroidManifest.xml文件中相关的<activity>条目中将android:noHistory属性设置为"true"来实现此操作。例如:

<activity
    android:name=".AnyActivity"
    android:noHistory="true" />

13
这是做这件事的最佳方式。 - shkschneider
24
你可以等效地使用FLAG_ACTIVITY_NO_HISTORY - Timmmm
38
我该如何从代码中实现这个功能?因为我不想一直手动操作。我只想在某些条件下将特定的活动从历史记录中删除。 - Namratha
9
请注意,使用noHistory属性会使该活动被结束,这可能会导致G+登录等出现意外行为。请参见: https://dev59.com/a-o6XIcBkEYKwwoYKRLd 我的应用程序一直崩溃而没有任何痕迹,找到这个错误花了我一些时间。 - Acapulco
13
由于这是一个链接,因此很难复制所需的标记,因此提供一个易于复制的标记:android:noHistory="true"。请注意,该标记将应用程序活动从后堆栈中删除,以便在返回时不会再次启动该活动。 - MirroredFate
显示剩余9条评论

139

当启动下一个活动时,您可以使用转发来从活动堆栈中删除前一个活动。在APIDemos中有一个示例,点击此处查看,但基本上你所要做的就是在调用startActivity()之后立即调用finish()


1
嗨,丹尼尔,我已经阅读了这个例子,但是它只会清除历史堆栈中的一个活动,不是吗?如果我的堆栈看起来像A、B,而我正在启动C,我希望C成为新的根,并完全清除A和B。在SDK示例中,从B调用finish()会让我得到一个A、C的堆栈,是吗?谢谢。 - Mark
1
你可以在启动B时清除A,然后在启动C时清除B;但是,如果你想要从B返回到A,那么我认为这样做就不可行了。如果这是一个问题,我需要再仔细考虑一下。 - Dan Lew
3
为了实现你的目标,你需要使用标志:FLAG_ACTIVITY_CLEAR_TOP。希望这有所帮助。 - Francisco Junior
3
FLAG_ACTIVITY_CLEAR_TOP 无法起到作用,因为C不在返回栈中。 - Timmmm
1
@DanielLew 在APIDemos中有很多示例。你能告诉我示例的名称吗? - Stephane
显示剩余4条评论

55

47
请注意,这不会为您要启动的活动设置历史记录。如果要在启动活动时不保留历史记录,只需在清单文件中将其设置为 android:noHistory="true" 即可。 - georgiecasey
1
这个很好用。使用 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) 来设置此标志。 - vovahost
1
@georgiecasey 这在有时想要保留历史记录,有时又不想保留历史记录时非常有用。 - Vihaan Verma

25

这可能不是最理想的方法。如果有更好的方法,我期待着实施它。这是我使用早于版本11的sdk完成此特定任务的方式。

在你想要在清除时间时退出的每个类中,你需要做以下操作:

    ... interesting code stuff ...
    Intent i = new Intent(MyActivityThatNeedsToGo.this, NextActivity.class);
    startActivityForResult(i, 0);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == R.string.unwind_stack_result_id) {
        this.setResult(R.string.unwind_stack_result_id);
        this.finish();
    }
}

那么需要从栈中弹出一系列的元素时,只需在想要启动它时调用此函数:

NextActivity.this.setResult(R.string.unwind_stack_result_id);
NextActivity.this.finish();

那么这些活动就不在堆栈上了!
记住,您可以启动一个活动,然后开始在其后清理,执行不遵循单个(ui)线程。


如果有人以后遇到这个问题,我认为了解以下信息可能很重要:如果您旋转了屏幕,当您从堆栈中弹出最后一个活动时,Android会帮助您重新启动应用程序。请注意这一点! - Travis
如果您只想根据用户在下一个活动中的操作有条件地从堆栈中删除活动,则此答案是不错的选择。+1 - theMothaShip

23

在API 11之前有效的一种方法是先启动ActivityGameMain,然后在该Activity的onCreate方法中启动ActivitySplashScreen。由于你过早地调用了startActivity方法,所以ActivityGameMain不会立即出现。

然后,您可以通过在Intent上设置以下标志来在启动ActivityGameMain时清除堆栈:

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);

你还必须将此添加到 ActivitySplashScreen 中:

@Override
public void onBackPressed() {
    moveTaskToBack(true);
}

为了避免按下返回键时回到您的ActivityGameMain,我建议在AndroidManifest.xml中将其设置为noHistory。然后将goBackPressed代码放在ActivitySplashScreenSignUp类中。

不过,我发现有几种方法会破坏这个设置,在ActivitySplashScreenSignUp显示时从通知中启动另一个应用程序,此时返回历史记录不会被重置。

唯一真正的解决方案是在API 11中:

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);

20

我用这个方法。

Intent i = new Intent(MyOldActivity.this, MyNewActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
startActivity(i);

谢谢。如果我的菜单上有一个主页按钮,我不希望堆叠的活动在用户从那里点击主页后出现,那么我发现这很有帮助。 - ghitesh

8

我知道我来晚了(距离问题提出已经两年了),但是我通过拦截返回按钮按下来实现了这个功能。我并没有针对特定的活动进行检查,而是只关注数量,如果少于3个,就直接发送应用程序到后台(暂停应用程序并将用户返回到启动前运行的内容)。我检查小于三是因为我只有一个介绍屏幕。此外,我检查数量,因为我的应用程序允许用户通过菜单返回到主屏幕,因此如果堆栈中除了介绍屏幕之外还有其他活动,这允许他们像正常情况下一样向后备份。

//We want the home screen to behave like the bottom of the activity stack so we do not return to the initial screen
//unless the application has been killed. Users can toggle the session mode with a menu item at all other times.
@Override
public void onBackPressed() {
    //Check the activity stack and see if it's more than two deep (initial screen and home screen)
    //If it's more than two deep, then let the app proccess the press
    ActivityManager am = (ActivityManager)this.getSystemService(Activity.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(3); //3 because we have to give it something. This is an arbitrary number
    int activityCount = tasks.get(0).numActivities;

    if (activityCount < 3)
    {
        moveTaskToBack(true);
    }
    else
    {
        super.onBackPressed();
    }
}

8
在清单文件中,您可以添加以下内容:
android:noHistory="true"

<activity
android:name=".ActivityName"
android:noHistory="true" />

你可以使用以下方式进行调用。
finish()

在调用startActivity(..)后立即执行


2
最好使用活动标志而不是将内容放在清单中。 - Trevor Hart

6
只需在清单文件中设置noHistory="true"即可。这将使活动从后台堆栈中移除。

4

太不可思议了,竟然没有人提到这个优雅的解决方案。这应该是被接受的答案。

SplashActivity -> AuthActivity -> DashActivity

if (!sessionManager.isLoggedIn()) {
    Intent intent = new Intent(context, AuthActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
    context.startActivity(intent);
    finish();
} else {
   Intent intent = new Intent(context, DashActivity.class);
   context.startActivity(intent);
    finish();
}

关键在于在中间的Activity中使用intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);。一旦中间的链接被断开,DashActivity将成为堆栈中的第一个和最后一个。 android:noHistory="true"是一个糟糕的解决方案,因为它会在依赖Activity作为回调(例如onActivityResult)时造成问题。这是推荐的解决方案,应该被采纳。

2
那个标志听起来太好了,以至于让人难以置信。FLAG_ACTIVITY_NO_HISTORY 的工作方式很好,符合预期,直到将应用程序放到后台,然后再次将其调回前台时,同时在堆栈顶部具有标志的活动。当进入后台时,该活动被销毁。当返回应用程序时,将会看到堆栈中的上一个活动。我绝对不希望出现这种行为。 - NeverwinterMoon

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