清除整个历史堆栈并在 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 上启动一个新活动的主要内容,如果未能解决你的问题,请参考以下文章
清除 Activity Stack 并在 android 中启动新的 Activity
Ionic 4 Angular 路由器导航并清除上一页的堆栈/历史记录