我的应用程序中的所有活动都要求用户登录才能查看。用户几乎可以从任何活动中注销。这是应用程序的要求。在任何时候,如果用户注销,我想将用户发送到登录Activity
。此时我希望此活动位于历史堆栈的底部,以便按“后退”按钮可将用户返回到Android的主屏幕。
我已经看到这个问题问了几个不同的地方,都回答了类似的答案(我在这里概述),但我想在这里提出收集反馈。
我已经尝试通过将Intent
标志设置为FLAG_ACTIVITY_CLEAR_TOP
来打开Login活动,这似乎是在文档中概述的,但是没有实现我将Login活动放在历史堆栈底部并且阻止用户导航回以前看到的登录活动。我也尝试使用android:launchMode="singleTop"
作为清单中的Login活动,但这也没有实现我的目标(并且似乎无论如何都没有效果)。
我相信我需要清除历史堆栈,或者完成之前打开的所有活动。
一种选择是让每个活动的onCreate
检查登录状态,如果没有登录,则检查finish()
。我不喜欢这个选项,因为后退按钮仍可供使用,当活动靠近时自动导航。
下一个选项是维护一个LinkedList
引用所有可以从任何地方静态访问的开放活动(可能使用弱引用)。注销时,我将访问此列表并迭代所有先前打开的活动,在每个活动上调用finish()
。我很快就会开始实施这种方法。
然而,我宁愿使用一些Intent
标志技巧来实现这一目标。我很高兴发现我可以满足我的应用程序的要求,而不必使用我上面概述的两种方法中的任何一种。
有没有办法通过使用Intent
或清单设置来实现这一点,或者是我的第二个选择,保持开放活动的LinkedList
是最佳选择?或者我还有另一种选择吗?
我可以建议你另一种方法恕我直言更健壮。基本上,您需要向需要保持登录状态的所有活动广播注销消息。因此,您可以使用sendBroadcast
并在所有Actvities中安装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 **//
}
这是我在我的应用程序中提出的解决方案。
在我的LoginActivity中,成功处理登录后,我会根据API级别以不同方式启动下一个登录。
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);
}
最后,在处理任何其他Activity中的注销后:
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);
当使用Gingerbread时,如果我按下MainActivity中的后退按钮,则会立即隐藏LoginActivity。在Honeycomb之后,我只是在处理登录后完成LoginActivity,并在处理注销后正确地重新创建。
有时finish()
不工作
我已经解决了这个问题
finishAffinity()
我建议采用不同的方法解决这个问题。也许它不是最有效的,但我认为这是最容易应用的,并且只需要很少的代码。在第一个活动中编写下一个代码(在我的情况下,登录活动)将不会让用户在注销后返回先前启动的活动。
@Override
public void onBackPressed() {
// disable going back to the MainActivity
moveTaskToBack(true);
}
我假设LoginActivity刚刚在用户登录后完成,因此他以后不能通过按后退按钮返回到它。相反,用户必须按下应用程序内的注销按钮才能正确注销。这个注销按钮将实现的是一个简单的意图如下:
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
finish();
欢迎所有建议。
使用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);
}
@doreamon提供的解决方案适用于所有情况,除了一个:
如果登录后,Killing Login屏幕用户直接导航到中间屏幕。例如在A-> B-> C的流程中,导航如:登录 - > B - > C - >按快捷键返回主页。使用FLAG_ACTIVITY_CLEAR_TOP仅清除C活动,因为主页(A)不在堆栈历史记录中。按回屏幕将返回B.
为了解决这个问题,我们可以保留一个活动堆栈(Arraylist),当按下home时,我们必须杀死这个堆栈中的所有活动。
可以通过在SharedPreferences或Application Activity中管理标志来实现。
在启动应用程序时(在启动画面上)设置flag = false;在Logout Click事件中,只需将标志设置为true,并在每个活动的OnResume()中,检查flag是否为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);
一个选项是让每个活动的onCreate检查登录状态,如果没有登录则完成()。我不喜欢这个选项,因为后退按钮仍可供使用,当活动靠近时自动导航。
你想要做的是在你的onStop()或onPause()方法上调用logout()和finish()。这将迫使Android在重新启动活动时调用onCreate(),因为它不会再将其放入其活动的堆栈中。然后如你所说,在onCreate()中检查登录状态,如果没有登录则转发到登录屏幕。
您可以做的另一件事是检查onResume()中的登录状态,如果没有登录,则完成()并启动登录活动。
一个新的Android程序员花了一天时间研究这个问题并阅读所有这些StackOverflow线程似乎是一种成熟的仪式。我现在是新成立的,我离开这里看看我的谦逊经历,以帮助未来的朝圣者。
首先,根据我的研究(as of September 2012).
没有明显或直接的方法来做到这一点你认为你可以简单的startActivity(new Intent(this, LoginActivity.class), CLEAR_STACK)
但没有。
您可以使用startActivity(new Intent(this, LoginActivity.class))
执行FLAG_ACTIVITY_CLEAR_TOP
- 这将导致框架向下搜索堆栈,找到您之前的LoginActivity原始实例,重新创建它并清除其余的(向上)堆栈。由于Login可能位于堆栈的底部,因此您现在有一个空堆栈,而Back按钮就会退出应用程序。
但是 - 这只有在您之前在堆栈底部保留原始LoginActivity实例时才有效。如果像许多程序员一样,一旦用户成功登录就选择finish()
LoginActivity
,那么它不再位于堆栈的基础上并且FLAG_ACTIVITY_CLEAR_TOP
语义不适用...你最终会创建一个新的LoginActivity
现有的堆栈。这几乎肯定不是你想要的(用户可以“退回”登录前一个屏幕的奇怪行为)。
因此,如果你以前有finish()
的LoginActivity
,你需要寻求一些机制来清理你的堆栈,然后开始一个新的LoginActivity
。似乎@doreamon
在这个帖子中的答案是最好的解决方案(至少对我不起眼的看法):
https://stackoverflow.com/a/9580057/614880
我强烈怀疑你是否将LoginActivity保持活着这一棘手的问题导致了很多这种混乱。
祝好运。
UPDATE
超级finishAffinity()
方法将有助于减少代码,但实现相同。它将完成当前活动以及堆栈中的所有活动,如果您在片段中,则使用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建议,或其他一些技巧。
(另请注意:要使用此功能,您必须传入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听起来像你想要的:清除整个堆栈,除了正在启动的活动,所以Back按钮退出应用程序。然而正如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();
}
很多答案。也许这个也会有所帮助 -
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);
接受的解决方案不正确,它有问题,因为使用广播接收器对这个问题不是一个好主意。如果您的活动已经调用了onDestroy()方法,那么您将无法获得接收器。最佳解决方案是在共享首选项上使用布尔值,并在您的activty的onCreate()方法中进行检查。如果在用户未登录时不应该调用它,则完成活动。这是示例代码。如此简单,适用于各种条件。
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);