清除整个历史堆栈并在 Android 上启动一个新活动

Posted

技术标签:

【中文标题】清除整个历史堆栈并在 Android 上启动一个新活动【英文标题】:Clear the entire history stack and start a new activity on Android 【发布时间】:2011-03-29 05:46:05 【问题描述】:

是否可以在堆栈上启动一个活动,清除它之前的整个历史记录?

情况

我有一个 A->B->C 或 B->C 的活动堆栈(屏幕 A 选择用户令牌,但许多用户只有一个令牌)。

在屏幕 C 中,用户可能采取了使屏幕 B 无效的操作,因此应用程序希望将他们带到屏幕 A,而不管它是否已经在堆栈中。屏幕 A 应该是我的应用程序堆栈中的唯一项目。

备注

还有许多其他类似的问题,但我还没有找到任何可以回答这个确切问题的东西。我尝试调用getParent().finish() - 这总是会导致空指针异常。 FLAG_ACTIVITY_CLEAR_TOP 仅在 Activity 已经在堆栈中时才有效。

【问题讨论】:

【参考方案1】:

在 API 级别 11 中,为此添加了一个新的 Intent 标志:Intent.FLAG_ACTIVITY_CLEAR_TASK

为了澄清,使用这个:

Java

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

科特林

intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

不幸的是 API lvl "DontHackandroidLikeThis" solution 确实是纯粹的骇客。你不应该那样做。 :)

编辑: 根据 @Ben Pearson 的评论,对于 API IntentCompat 类来实现相同的目的。可以使用IntentCompat.FLAG_ACTIVITY_CLEAR_TASK 标志来清除任务。因此,您也可以支持 pre API level 11。

【讨论】:

澄清一下,使用这个:intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 没有 Intent.FLAG_ACTIVITY_NEW_TASK 应用程序有时会在 android 4 上自行关闭 IntentCompat 现在也有一个清除任务的标志,因此您可以支持 pre API level 11 - developer.android.com/reference/android/support/v4/content/… IntentCompat.FLAG_ACTIVITY_CLEAR_TASK 在 API 级别 developer.android.com/reference/android/support/v4/content/… IntentCompat 的标志只是为了避免崩溃,但并没有像@David 所说的那样做任何事情。【参考方案2】:

案例1:只有两个activity A和B:

这里的 Activity 流程是 A->B 。从 B 点击后退按钮时,我们需要关闭应用程序,然后在从 A 启动 Activity B 时只需调用 finish() 这将阻止 android 将 Activity A 存储到 Backstack.eg 中活动 A 是应用程序的加载/启动屏幕。

Intent newIntent = new Intent(A.this, B.class);
startActivity(newIntent);
finish();

案例2:多于两个活动:

如果有像 A->B->C->D->B 这样的流程,并且在来自 Activity D 时单击 Activity B 中的返回按钮。在这种情况下,我们应该使用。

Intent newIntent = new Intent(D.this,B.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent);

这里 Activity B 将从后台堆栈而不是新实例启动,因为 Intent.FLAG_ACTIVITY_CLEAR_TOP 和 Intent.FLAG_ACTIVITY_NEW_TASK 清除堆栈并使其成为顶部。所以当我们按下返回按钮时,整个应用程序将被终止。

【讨论】:

这对我有用。我把这些标志放在所有活动中。在这些活动中,后退按钮可以完美地转到上一个活动,并且在主活动中 Intent intent = new Intent(Intent.ACTION_MAIN);意图.addCategory(意图.CATEGORY_HOME);意图.addFlags(意图.FLAG_ACTIVITY_CLEAR_TOP);意图.addFlags(意图.FLAG_ACTIVITY_NEW_TASK);开始活动(意图);结束();整个应用程序已关闭,仍在内存中但没有活动,如果您重新启动应用程序,则会进入启动屏幕:) 这应该是最好的答案。如果有人和我有同样的情况:A->B->C->D->E->(B) From E->B 应该有结果:A->B【参考方案3】:

Android 的较新版本 >= API 16 使用 finishAffinity()

方法适用于 >= API 16。

Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
和启动新Activity一样,清空所有栈。 或重新启动到 MainActivity/FirstActivity。

【讨论】:

这成功了,标志在 4.x.x 上对我不起作用,而且效果很好!谢谢 如果您的目标是完成以下所有活动(包括当前活动)并在自己的任务中开始新活动,这似乎是正确答案。【参考方案4】:

我也为此花费了几个小时......并同意 FLAG_ACTIVITY_CLEAR_TOP 听起来像您想要的:清除整个堆栈,除了正在启动的活动,因此后退按钮退出应用程序。然而,正如 Mike Repass 所提到的, FLAG_ACTIVITY_CLEAR_TOP 仅在您启动的活动已经在堆栈中时才有效;当活动不存在时,标志不会做任何事情。

怎么办?将正在启动的活动放入带有 FLAG_ACTIVITY_NEW_TASK 的堆栈中,这使得该活动成为历史堆栈上新任务的开始。然后添加 FLAG_ACTIVITY_CLEAR_TOP 标志。

现在,当 FLAG_ACTIVITY_CLEAR_TOP 在堆栈中查找新 Activity 时,它会在那里并在其他所有内容被清除之前被拉起。

这是我的注销功能; View 参数是函数附加到的按钮。

public void onLogoutClick(final View view) 
    Intent i = new Intent(this, Splash.class);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    startActivity(i);
    finish();

【讨论】:

您的意思是 CLEAR_TASK 而不是 CLEAR_TOP?【参考方案5】:

在您开始一项新活动后,立即使用startActivity,确保您调用finish(),以便当前活动不会堆叠在新活动后面。

【讨论】:

+1 很好的解决方案,可以防止在特定情况下仅将一项活动放入历史堆栈。 如果堆栈中有多个活动则不起作用,完成只会清除前一个活动,但不会清除其他活动......【参考方案6】:

您不应该更改堆栈。 Android 后退按钮应该像在网络浏览器中一样工作。

我能想到一个办法,但它是一个相当的黑客。

通过将活动添加到AndroidManifest 来创建您的活动singleTask 示例:

<activity android:name=".activities.A"
          android:label="@string/A_title"
          android:launchMode="singleTask"/>

<activity android:name=".activities.B"
          android:label="@string/B_title"
          android:launchMode="singleTask"/>

扩展Application,它将保存去哪里的逻辑。

例子:

public class DontHackAndroidLikeThis extends Application 

  private Stack<Activity> classes = new Stack<Activity>();

  public Activity getBackActivity() 
    return classes.pop();
  

  public void addBackActivity(Activity activity) 
    classes.push(activity);
  

从 A 到 B:

DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(A.class); 
startActivity(this, B.class);

从 B 到 C:

DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(B.class); 
startActivity(this, C.class);

在 C 中:

If ( shouldNotGoBackToB() ) 
  DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
  app.pop();

并将返回按钮从堆栈中处理为pop()

再一次,你不应该这样做:)

【讨论】:

最后我决定保持 Stack 不变,只是告诉用户他们当前的屏幕无效 非常令人沮丧的是,android 已经不允许我们以这种方式管理活动堆栈。我很想在我未来的安卓应用中使用这个解决方案。 只是为了清楚为什么不应该使用它:它是创建内存泄漏的好方法。在某些时候,操作系统可能会决定终止后台活动,但由于Application 获取了他们的实例,操作系统将无法从被破坏的活动中释放剩余的 RAM。 @Arhimed 还有其他问题吗?内存泄漏可以通过只保留弱引用来修补。 @Navin 是的,使用弱引用可以避免泄漏,但是如果在 GC 之后没有实时的 Activity 引用,那么整个方法就没有用了。再说一遍 - 不要这样做,这对 Android 来说是错误的方法。【参考方案7】:

高级可重用 Kotlin:

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

intent.flags = FLAG_ACTIVITY_NEW_TASK or 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

【讨论】:

【参考方案8】:

试试这个:

Intent logout_intent = new Intent(DashboardActivity.this, LoginActivity.class);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(logout_intent);
finish();

【讨论】:

【参考方案9】:

试试下面的代码,

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

【讨论】:

如果我使用这样的活动再次更新调用 api 但之前存在的所有 statck 被清除【参考方案10】:

对我来说没有上述方法不起作用。

只需执行此操作即可清除所有以前的活动

finishAffinity() // if you are in fragment use activity.finishAffinity()
Intent intent = new Intent(this, DestActivity.class); // with all flags you want
startActivity(intent)

【讨论】:

【参考方案11】:
Intent i = new Intent(MainPoliticalLogin.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);

【讨论】:

【参考方案12】:

有时您的 android 模拟器可能无法连接 eclipse DDMS 工具并要求手动启动 adb。在这种情况下,您可以使用命令提示符启动或停止 adb。

【讨论】:

有时您的安卓模拟器可能无法连接 eclipse DDMS 工具并要求 adb 手动启动。在这种情况下,您可以使用命令提示符启动或停止 adb。 Intent i = new Intent(OldActivity.this, NewActivity.class); // 设置新任务并清除标志 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) startActivity(i);【参考方案13】:

我发现 hack 太简单了,只需在 AndroidManifest 中添加新元素即可:-

<activity android:name=".activityName"
          android:label="@string/app_name"
          android:noHistory="true"/>

android:noHistory 将从堆栈中清除您不需要的活动。

【讨论】:

如果您在此 Activity 中请求许可,此方法可能会导致 Android 6.0+ 出现问题。

以上是关于清除整个历史堆栈并在 Android 上启动一个新活动的主要内容,如果未能解决你的问题,请参考以下文章

Android:从历史堆栈中清除旧的活动实例

清除 Activity Stack 并在 android 中启动新的 Activity

Ionic 4 Angular 路由器导航并清除上一页的堆栈/历史记录

导航到Android中的另一个片段后如何清除导航堆栈

Android:如何在按下按钮时关闭一个活动并在按下另一个按钮时关闭整个后台堆栈? [复制]

在 Swift 中清除导航堆栈