在注销时,清除活动历史堆栈,防止“后退”button打开已login的活动

我的应用程序中的所有活动都需要用户login才能查看。 用户几乎可以从任何活动注销。 这是应用程序的要求。 在任何时候,如果用户注销,我想发送用户到loginActivity 。 在这一点上,我想这个活动是在历史堆栈的底部,所以按“返回”button返回到Android的主屏幕的用户。

我已经看到这个问题问几个不同的地方,都回答了类似的答案(我在这里概述),但我想在这里提出来收集反馈意见。

我已经尝试通过将其Intent标志设置为FLAG_ACTIVITY_CLEAR_TOP来打开Login活动,这似乎是按照文档中所述做的,但是并没有达到将Login活动放置在历史堆栈底部的目的,并且阻止了用户导航回到先前看到的login活动。 我也尝试使用android:launchMode="singleTop"作为清单中的login活动,但是这也不能完成我的目标(也似乎没有效果)。

我相信我需要清除历史堆栈,或完成所有以前打开的活动。

一种select是让每个活动的onCreate检查login状态,如果没有login,则finish() 。 我不喜欢这个选项,因为后退button仍然可以使用,在活动closures时返航。

下一个选项是维护一个链接列表,引用所有可以从任何地方静态访问的打开活动(也许使用弱引用)。 在注销时,我将访问这个列表并遍历所有以前打开的活动, finish()在每个活动上调用finish() 。 我很可能会很快开始实施这个方法。

但是,我宁愿使用一些Intent标志欺骗来实现这一点。 我会很高兴地发现,我可以满足我的应用程序的要求,而无需使用上述两种方法之一。

有没有办法通过使用Intent或清单设置,或者是我的第二个select,维护打开活动的LinkedList是最好的select? 还是有另一种select,我完全忽略了?

我可以build议你另一种方法恕我直言,更健壮。 基本上,您需要向所有需要保持login状态的活动广播注销消息。 因此,您可以使用sendBroadcast并在您所有的sendBroadcast安装BroadcastReceiver 。 像这样的东西:

 /** on your logout method:**/ Intent broadcastIntent = new Intent(); broadcastIntent.setAction("com.package.ACTION_LOGOUT"); sendBroadcast(broadcastIntent); 

接收器(担保活动):

 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /**snip **/ IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("com.package.ACTION_LOGOUT"); registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d("onReceive","Logout in progress"); //At this point you should start the login activity and finish this one finish(); } }, intentFilter); //** snip **// } 

一个新的Android程序员花了一天的时间来研究这个问题,并阅读所有这些StackOverflow线程。 我现在是新发起的,我在这里留下我谦卑的经历,帮助未来的朝圣者。

首先,根据我的研究(as of September 2012). ,没有明显或直接的方法来做到这一点(as of September 2012). 你会认为你可以简单的startActivity(new Intent(this, LoginActivity.class), CLEAR_STACK)没有

你可以使用startActivity(new Intent(this, LoginActivity.class))执行startActivity(new Intent(this, LoginActivity.class)) – 这将导致框架search堆栈,find你以前的LoginActivity原始实例,重新创build它并清除(向上)堆栈的其余部分。 而且由于Login大概在堆栈的底部,现在你有一个空的堆栈,Backbutton只是退出应用程序。

但是 – 这只有在您之前将LoginActivity的原始实例保留在堆栈底部的情况下才有效。 如果像许多程序员一样,当用户成功login后,您selectfinish() LoginActivity ,那么它不再位于堆栈的基础上,并且FLAG_ACTIVITY_CLEAR_TOP语义不适用于您最终创build一个新的LoginActivity现有堆栈的顶部。 这几乎肯定不是你想要的(奇怪的行为,用户可以退出login到前一个屏幕的方式)。

所以,如果你以前finish()LoginActivity ,那么你需要采取一些机制来清除你的堆栈,然后开始一个新的LoginActivity 。 这似乎是@doreamon在这个主题中的答案是最好的解决scheme(至less对我的谦逊的眼睛):

https://stackoverflow.com/a/9580057/614880

我强烈怀疑,你是否离开LoginActivity的棘手暗示造成了很多这种混淆。

祝你好运。

UPDATE

超级finishAffinity()方法将有助于减less代码,但实现相同。 它将完成当前活动以及堆栈中的所有活动,使用getActivity().finishAffinity()如果您在片段中)。

 finishAffinity(); startActivity(new Intent(mActivity, LoginActivity.class)); 

原来的答案

假设LoginActivity – > HomeActivity – > … – > SettingsActivity调用signOut():

 void signOut() { Intent intent = new Intent(this, HomeActivity.class); intent.putExtra("finish", true); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // To clean up all activities startActivity(intent); finish(); } 

HomeActivity:

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); boolean finish = getIntent().getBooleanExtra("finish", false); if (finish) { startActivity(new Intent(mContext, LoginActivity.class)); finish(); return; } initializeView(); } 

这对我有用,希望对你也有帮助。 🙂

如果您使用API​​ 11或更高版本,则可以尝试以下操作: FLAG_ACTIVITY_CLEAR_TASK – 您似乎正在解决您遇到的问题。 很明显,API 11之前的人群将不得不使用一些组合来让所有活动检查额外的东西,就像@doreamon所暗示的那样,或者其他一些欺骗手段。

(另请注意:要使用此function,您必须通过FLAG_ACTIVITY_NEW_TASK

 Intent intent = new Intent(this, LoginActivity.class); intent.putExtra("finish", true); // if you are checking for this in your other Activities intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); finish(); 

我也花了几个小时…并同意FLAG_ACTIVITY_CLEAR_TOP听起来像你想要的:清除整个堆栈,除了正在启动的活动,所以后退button退出应用程序。 然而,正如Mike Repass所提到的, FLAG_ACTIVITY_CLEAR_TOP只有在您正在启动的活动已经在堆栈中时才会起作用。 当活动不在那里,旗子什么都不做。

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

现在,当FLAG_ACTIVITY_CLEAR_TOP去查找堆栈中的新活动时,它将会在那里被清除之前被拉起。

这是我的注销function; View参数是函数所附的button。

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

很多的答案。 可能是这个也会帮助 –

 Intent intent = new Intent(activity, SignInActivity.class) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); this.startActivity(intent); this.finish(); 

使用它应该对你有帮助。 稍微修改xbakesx的答案。

 Intent intent = new Intent(this, LoginActivity.class); if(Build.VERSION.SDK_INT >= 11) { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK); } else { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); } startActivity(intent); 

接受的解决scheme是不正确的,它有问题,因为使用广播接收机不是一个好主意这个问题。 如果你的activity已经调用了onDestroy()方法,你将不会得到接收者。 最好的解决办法是在你的共享首选项上有一个布尔值,并在你的activity的onCreate()方法中检查它。 如果用户未login时不应调用,则完成活动。 这是示例代码。 这么简单,适用于任何情况。

 protected void onResume() { super.onResume(); if (isAuthRequired()) { checkAuthStatus(); } } private void checkAuthStatus() { //check your shared pref value for login in this method if (checkIfSharedPrefLoginValueIsTrue()) { finish(); } } boolean isAuthRequired() { return true; } 

所选的答案是聪明而棘手的。 以下是我如何做到的:

LoginActivity是任务的根源,在Manifest.xml中设置android:noHistory =“true” ; 假设你想从SettingsActivity注销,你可以这样做:

  Intent i = new Intent(SettingsActivity.this, LoginActivity.class); i.addFlags(IntentCompat.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(i); 

这是我在我的应用程序中提出的解决scheme。

在我的LoginActivity中,成功处理login之后,根据API级别,我开始下一个login。

 Intent i = new Intent(this, MainActivity.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { startActivity(i); finish(); } else { startActivityForResult(i, REQUEST_LOGIN_GINGERBREAD); } 

然后在我的LoginActivity的onActivityForResult方法中:

 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB && requestCode == REQUEST_LOGIN_GINGERBREAD && resultCode == Activity.RESULT_CANCELED) { moveTaskToBack(true); } 

最后,在任何其他活动中处理注销之后:

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

当在姜饼,如果我按MainActivity后退button,LoginActivity立即隐藏。 在Honeycomb和更高版本中,我只是在处理login后完成LoginActivity,并且在处理注销后正确地重新创build。

用StartActivityForResult开始你的活动,当你注销时设置你的结果,并根据你的结果完成你的活动

 intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivityForResult(intent, BACK_SCREEN); @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case BACK_SCREEN: if (resultCode == REFRESH) { setResult(REFRESH); finish(); } break; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog alertDialog = builder.create(); alertDialog .setTitle((String) getResources().getText(R.string.home)); alertDialog.setMessage((String) getResources().getText( R.string.gotoHome)); alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { setResult(REFRESH); finish(); } }); alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }); alertDialog.show(); return true; } else return super.onKeyDown(keyCode, event); } 

解决scheme@doreamon提供的工作正常的情况下,除了一个:

如果login后,“杀戮login”屏幕用户直接导航到中间屏幕。 例如在A-> B-> C的stream程中,浏览如下:login – > B – > C – >按快捷键回到主页面。 使用FLAG_ACTIVITY_CLEAR_TOP只会清除C活动,因为主页(A)不在堆栈历史logging上。 按下屏幕上的button会使我们回到B.

为了解决这个问题,我们可以保留一个活动堆栈(Arraylist),当home被按下时,我们必须杀死堆栈中的所有活动。

通过pipe理SharedPreferences或Application Activity中的标志是可能的。

在启动应用程序(在启animation面)设置标志=假; 在Logout Click事件中,只需将标志设置为true,并在每个活动的OnResume()中检查标志是否为true,然后调用finish()。

它像一个魅力:)

点击注销,你可以打电话给这个

 private void GoToPreviousActivity() { setResult(REQUEST_CODE_LOGOUT); this.finish(); } 

先前的Activity的onActivityResult()再次调用上面的代码,直到完成所有的活动。

这对我工作:

  // After logout redirect user to Loing Activity Intent i = new Intent(_context, MainActivity.class); // Closing all the Activities i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // Add new Flag to start new Activity i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Staring Login Activity _context.startActivity(i); 

有时finish()不起作用

我已经解决了这个问题

finishAffinity()

一种select是让每个活动的onCreate检查login状态,如果没有login,则完成()。 我不喜欢这个选项,因为后退button仍然可以使用,在活动closures时返航。

你想要做的是在你的onStop()或onPause()方法上调用logout()和finish()。 这将迫使Android在活动重新启动时调用onCreate(),因为它不再将其放在活动的堆栈中。 然后按照你的说法,在onCreate()检查login状态,并转发到login屏幕,如果没有login。

你可以做的另一件事是在onResume()中检查login状态,如果没有login,finish()并启动login活动。