ChatGPT解决这个技术问题 Extra ChatGPT

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

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

情况

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

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

笔记

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


C
Community

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

只是为了澄清,使用这个:

爪哇

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 <= 10,我还没有找到一个干净的解决方案。 "DontHackAndroidLikeThis" solution 确实是纯粹的骇客。你不应该那样做。 :)

编辑:根据@Ben Pearson 的评论,对于 API <=10,现在可以使用 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 级别 < 的设备上被忽略。 10. developer.android.com/reference/android/support/v4/content/…
IntentCompat 的标志只是为了避免崩溃,但没有像@David 所说的那样做任何事情。
m
monish george

案例1:只有两个活动A和B:

这里 Activity 流程是 A->B 。在从 B 单击后退按钮时,我们需要关闭应用程序,然后在从 A 启动 Activity B 时只需调用 finish() 这将阻止 android 将 Activity A 存储到 Backstack 中。例如,活动 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 意图的主 Activity = 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
k
karan

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

方法适用于 >= API 16。

Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);

和启动新的Activity一样,清空所有栈。

或重新启动到 MainActivity/FirstActivity。


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

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

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

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

这是我的注销功能; 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?
K
Keith Maurino

使用 startActivity 启动新活动后,请立即确保调用 finish(),以免当前活动堆积在新活动后面。


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

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

我可以想到一种方法来做到这一点,但它是一个相当黑客。

将您的活动添加到 AndroidManifest 示例中,使其成为 singleTask:

扩展应用程序,它将保存去哪里的逻辑。

例子:

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()

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


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

高级可重用 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
}

S
Sagar Zala

试试下面的代码,

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 被清除
P
Pang

尝试这个:

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();

A
Amir Hossein Ghasemi

对我来说,上述方法都不起作用。

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

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

J
Joaquin Iurchuk
Intent i = new Intent(MainPoliticalLogin.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);

R
RajeshkumarG

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


有时您的 android 模拟器可能无法连接 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);
T
Tauseef

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

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

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


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