Android:从返回栈中删除所有先前的活动

160

当我在我的个人资料活动中点击注销按钮时,我希望将用户带到登录页面,需要用户使用新的凭据。

因此我使用了以下代码:

Intent intent = new Intent(ProfileActivity.this,
        LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

在“登出”按钮的onButtonClick中。

但问题是,当我在登录界面上点击设备的返回按钮时,它会带我到ProfileActivity。我本来期望当我在LoginActivity上按下设备返回按钮时应用程序应该关闭。

我做错了什么?

我还在清单文件中为我的LoginActivity添加了android:launchMode="singleTop"

谢谢


@GauravVashisth 我只是在遵循这个解决方案 https://dev59.com/62025IYBdhLWcg3wyZIn - Archie.bpgc
@abbas.aniefa 那个解决方案有点复杂。这是清除所有后退堆栈的唯一方法吗?因为我有30多个活动,所以我应该为它们编写这个广播代码吗? - Archie.bpgc
试一下这个方法:http://stackoverflow.com/questions/10961481/closing-previous-pages-in-an-application/10961508#10961508。使用广播是更好的解决方案。 - abbas.aniefa
你可以使用另一种方法来注销,一旦注销,将一个标志位存储在sharedpreferences中,在每个活动的onRestart()方法中检查这个变量的值,如果设置为true,可以结束当前活动。所以无论有多少个活动在后台打开,都能完成所有的活动。 - Hiren Dabhi
您的原始代码实际上适用于API级别11或更高版本,只需要进行微调即可。您只需要将标志集中在单个调用中:intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 我从这个问题中得到了答案:https://dev59.com/5XA75IYBdhLWcg3wGlAa - jokeefe
可能是Android:清除活动堆栈的重复问题。 - Mehdi Dehghani
18个回答

382

我提供的解决方案对我起了作用:

Java

Intent i = new Intent(OldActivity.this, NewActivity.class);
// set the new task and clear flags
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);

Kotlin

val i = Intent(this, NewActivity::class.java)
// set the new task and clear flags
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(i)

然而,它需要API等级>=11。


小伙子们,看一下 FLAG_ACTIVITY_CLEAR_TASK 的文档。这是官方的方法,不需要改变应用程序中的所有活动。 - AlikElzin-kilaka
这对通知无效。https://dev59.com/KWAf5IYBdhLWcg3wOQq2 - Sam
1
我将最低API设置为16,所以对我来说这完全正常。然而,我注意到当API> 21时视图执行有点缓慢,而对于API <21则很流畅。有人遇到同样的问题吗? - Red M
1
这将为活动创建一个新的任务。 - Rohit
@kgiannakakis 毫无疑问,如果我们只有一个任务(Activity Back stack),解决方案是有效的,但是当我们有多个任务(Activity stack)时,它就不起作用了。例如,假设我为我的应用程序A、B、C和D创建了四个活动。假设我有两个Activity back stacks A->B->C(后台)和D->A->B(前台),如果我从当前堆栈(D->A->B)中使用您建议的意图过滤器调用活动A,会发生什么?它会清除我的当前堆栈(D->A->B),并打开活动A,当我按返回键时,它会关闭应用程序,但如果我按最近使用的应用程序按钮,我可以看到我的应用程序的两个后退堆栈。 - Rahul Pareta

32

这里提供一种解决方案,可以在使用注销按钮时清除您的应用程序中的所有活动。

每次启动一个Activity时,请像这样启动:

Intent myIntent = new Intent(getBaseContext(), YourNewActivity.class);
startActivityForResult(myIntent, 0);

当你想关闭整个应用程序时,请执行以下操作:

setResult(RESULT_CLOSE_ALL);
finish();

RESULT_CLOSE_ALL是一个终态全局变量,它具有唯一的整数值,用于指示您要关闭所有活动。

然后定义每个活动的onActivityResult(...)回调函数,以便当一个活动以RESULT_CLOSE_ALL值返回时,它也调用finish()

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch(resultCode)
    {
    case RESULT_CLOSE_ALL:
        setResult(RESULT_CLOSE_ALL);
        finish();
    }
    super.onActivityResult(requestCode, resultCode, data);
}

这将导致一个级联效应,关闭您的所有活动。

然而,这是一种hack方法,并以startActivityForResult的方式使用,这不是其设计的目的。

也许更好的方法是使用广播接收器,如此处所示:

在注销时清除活动历史记录堆栈,防止“返回”按钮打开仅限登录的活动

还可以查看以下主题了解其他方法:

Android:清除后退堆栈

完成所有以前的活动


4
有大约30个历史活动,这是否可行? - Rakesh Gondaliya
4
如果你的历史记录中有30个活动,那么你可能需要考虑更改设计。30个活动似乎有点太多,Android可能会自动终止它们。 - Anup Cowkur
1
这似乎是一种不好的做法。@RakeshGondaliya的回答问题似乎是合理的。30可能只是代表“高”活动数量的数字。这个答案看起来更好:https://dev59.com/iXA75IYBdhLWcg3w9-Ox#3008684 - Aman Alam
这个答案很棒。你也可以使用EventBus库将事件发送到后台堆栈中的活动。https://github.com/greenrobot/EventBus - Stan
最好使用简单的“Intent.FLAG_ACTIVITY_CLEAR_TOP”标志与意图。 - Naveen Rao

27

finishAffinity() 在 API 16 中添加。在早期版本中,请使用 ActivityCompat.finishAffinity()。当你使用 Intent 启动任何活动并结束当前活动时,请改为使用 ActivityCompat.finishAffinity(),而不是 finish()。它将会结束当前活动之下的所有堆叠活动。这对我很有效。


3
实际上,这是 ActivityCompat.finishAffinity() - The Berga
finishAffinity() 方法会结束当前 Activity,并且结束同一亲和性(affinity)的所有 Activity。 - Neeraj Sewani

23

要完全清除活动栈,您需要使用TaskStackBuilder创建一个新的任务栈,例如:

Intent loginIntent = LoginActivity.getIntent(context);
TaskStackBuilder.create(context).addNextIntentWithParentStack(loginIntent).startActivities();

这不仅会创建一个新的干净任务栈,还将允许“向上”按钮正确地工作,如果您的LoginActivity具有父活动。


2
这是最好的方法。 - Paul Burke
1
“FLAG_ACTIVITY_NEW_TASK” 或者 “finishAffinity()” 对我来说都没有用。只有这个答案解决了问题。 - T.M
如果我通过返回按钮离开应用程序,然后通过应用程序图标返回,它会在我调用该方法的活动中打开。所以对我来说不起作用。 - Tgo1014

10

对我有效的方法

Intent intent = new Intent(getApplicationContext(), HomeActivity.class);
ComponentName cn = intent.getComponent();
Intent mainIntent = IntentCompat.makeRestartActivityTask(cn);
startActivity(mainIntent);

7
您可以尝试使用finishAffinity(),它会关闭所有当前活动并适用于Android 4.1及以上版本。

1
finishAffinity() 在 API 16 中添加。在之前的版本中,请使用 ActivityCompat.finishAffinity()。对我来说它很好用。 - Anurag Srivastava

6

我可以向你建议的一种解决方案是,在我的ProfileActivity清单中添加android:launchMode="singleTop"。当点击注销时,您可以重新启动LoginActivity以进行logoff。在注销时,您可以调用此方法。

Intent in = new Intent(Profile.this,Login.class);
                in.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(in);
                finish();

我做了这个,发生的情况是:当我点击返回按钮时,它会将我带回到我来到ProfileActivity的活动。 - Archie.bpgc
这可能是因为您通过调用新的意图而不是调用finish()来到ProfileActivity。您是如何从LoginActivty以外的其他活动来到ProfileActivity的? - Android
我也遇到了同样的问题。我认为当系统销毁登录意图时,比如由于内存不足,它可能无法正常工作。 - user1914692

6

对于API 11+,您可以像这样使用Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK

Intent intent = new Intent(this, MyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);

它将完全清除所有之前的活动,并启动新的活动。


4

以下内容适用于activity

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

移除CLEAR_TASK标记以在片段中使用。

我希望这对一些人有用。


额外的注释“为片段使用删除CLEAR_TASK标志”拯救了我。 你是一个伟大的人!感谢您终止了我2小时的调试时间 ;) - LostSoul

4

高级、可重用的 Kotlin:

您可以使用 setter 方法直接设置标志。在 Kotlin 中,or 是 Java 位或 |替代品

intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

如果你计划经常使用这个功能,请创建一个Intent扩展函数。

fun Intent.clearStack() {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

你可以在启动意图之前直接调用此函数。
intent.clearStack()

如果您需要在其他情况下添加额外的标志选项,请向扩展函数添加一个可选参数。
fun Intent.clearStack(additionalFlags: Int = 0) {
    flags = additionalFlags or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

1
太棒了!这应该得到更多的赞。 - Thái Quốc Toàn
不知何故,对于我来说,Android 7.1上的NEW_TASK、CLEAR_TOP和CLEAR_TASK都不起作用。返回按钮仍然会返回到先前的活动。唯一有效的关闭方式是调用所有未完成活动上的finish()方法,这种方式看起来很丑陋。无论是添加标志还是设置标志,都没有效果。同时,finishAffinity()和finishAndRemoveTask()也没有解决问题。 - George

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