我的应用程序中的所有活动都需要用户登录才能查看。用户几乎可以退出任何活动。这是应用程序的要求。在任何时候,如果用户注销,我想将用户发送到登录 Activity
。在这一点上,我希望这个活动位于历史堆栈的底部,以便按下“返回”按钮将用户返回到 Android 的主屏幕。
我在几个不同的地方看到过这个问题,所有的答案都相似(我在这里概述),但我想在这里提出它以收集反馈。
我尝试通过将其 Intent
标志设置为 FLAG_ACTIVITY_CLEAR_TOP
来打开登录活动,这似乎按照文档中的概述进行,但没有实现将登录活动放在历史堆栈底部的目标,并且防止用户导航回以前看到的登录活动。我还尝试将 android:launchMode="singleTop"
用于清单中的登录活动,但这也没有实现我的目标(而且似乎无论如何都没有效果)。
我相信我需要清除历史堆栈,或者完成所有以前打开的活动。
一种选择是让每个活动的 onCreate
检查登录状态,如果未登录则检查 finish()
。我不喜欢这个选项,因为后退按钮仍然可以使用,在活动自行关闭时导航回来。
下一个选项是维护对所有打开活动的引用的LinkedList
,这些活动可以从任何地方静态访问(可能使用弱引用)。注销时,我将访问此列表并遍历所有先前打开的活动,对每个活动调用 finish()
。我可能很快就会开始实施这种方法。
但是,我宁愿使用一些 Intent
标志技巧来完成此操作。我很高兴发现我可以满足我的应用程序的要求,而无需使用上面概述的两种方法中的任何一种。
有没有办法通过使用 Intent
或清单设置来实现这一点,或者我的第二个选项是保持打开的活动 LinkedList
是最佳选择吗?还是有另一个我完全忽略的选择?
我可以建议你另一种更强大的方法恕我直言。基本上,您需要向所有需要保持登录状态的活动广播注销消息。因此,您可以使用 sendBroadcast
并在您的所有 Activity 中安装 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).
您认为可以简单startActivity(new Intent(this, LoginActivity.class), CLEAR_STACK)
,但不行。
您可以使用 FLAG_ACTIVITY_CLEAR_TOP
执行 startActivity(new Intent(this, LoginActivity.class))
- 这将导致框架向下搜索堆栈,找到您之前的 LoginActivity 原始实例,重新创建它并清除(向上)堆栈的其余部分。由于 Login 可能位于堆栈的底部,因此您现在有一个空堆栈,并且 Back 按钮只是退出应用程序。
但是 - 这仅在您之前将 LoginActivity 的原始实例留在堆栈底部时才有效。如果像许多程序员一样,一旦用户成功登录,您选择 finish()
那个 LoginActivity
,那么它不再位于堆栈的基础上,并且 FLAG_ACTIVITY_CLEAR_TOP
语义不适用......您最终创建现有堆栈顶部的新 LoginActivity
。这几乎肯定不是您想要的(奇怪的行为,用户可以“返回”退出登录到前一个屏幕)。
因此,如果您以前finish()
使用过 LoginActivity
,则需要采用某种机制来清除堆栈,然后开始新的 LoginActivity
。似乎 @doreamon
在这个线程中的答案是最好的解决方案(至少在我谦虚的眼里):
https://stackoverflow.com/a/9580057/614880
我强烈怀疑你是否让 LoginActivity 活着的棘手影响导致了很多这种混乱。
祝你好运。
更新
super 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();
}
家庭活动:
@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();
finish();
可以防止在注销后返回。设置标志需要什么?
我也为此花费了几个小时......并同意 FLAG_ACTIVITY_CLEAR_TOP 听起来像您想要的:清除整个堆栈,除了正在启动的活动,因此“后退”按钮退出应用程序。然而正如 Mike Repass 提到的,FLAG_ACTIVITY_CLEAR_TOP 仅在您启动的活动已经在堆栈中时才有效;当活动不存在时,标志不会做任何事情。
该怎么办?使用 FLAG_ACTIVITY_NEW_TASK 将正在启动的 Activity 放入堆栈中,这会使该 Activity 成为历史堆栈上新任务的开始。 然后添加 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();
}
FLAG_ACTIVITY_CLEAR_TASK
很多答案。可能这个也有帮助-
Intent intent = new Intent(activity, SignInActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
this.finish();
Kotlin 版本——
Intent(this, SignInActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}.also { startActivity(it) }
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() 方法,您将不会获得接收者。最好的解决方案是在您的共享偏好上设置一个布尔值,并在您的活动的 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;
}
onResume()
方法中。因为当您按下后退按钮时,活动更有可能恢复而不是创建。
有时finish()
不工作
我已经解决了这个问题
finishAffinity()
这是我在我的应用程序中提出的解决方案。
在我的 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);
}
最后,在任何其他活动中处理注销后:
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,并在处理注销后正确地重新创建它。
我建议对这个问题采取不同的方法。也许它不是最有效的,但我认为它是最容易应用的,并且只需要很少的代码。在您的第一个活动(在我的情况下为登录活动)中编写下一个代码不会让用户在注销后返回到以前启动的活动。
@Override
public void onBackPressed() {
// disable going back to the MainActivity
moveTaskToBack(true);
}
我假设 LoginActivity 在用户登录后就完成了,这样他以后就不能通过按后退按钮返回它。相反,用户必须按下应用程序内的注销按钮才能正确注销。这个注销按钮将实现一个简单的意图,如下所示:
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
finish();
欢迎所有建议。
选择的答案既聪明又棘手。我是这样做的:
LoginActivity是任务的根Activity,在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);
这对我有用:
// 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);
使用 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 提供的解决方案适用于所有情况,但以下情况除外:
如果登录后,杀死登录屏幕用户直接导航到中间屏幕。例如,在 A->B->C 的流程中,导航如下:登录 -> B -> C -> 按快捷方式返回主页。使用 FLAG_ACTIVITY_CLEAR_TOP 仅清除 C 活动,因为 Home(A) 不在堆栈历史记录中。在 A 屏幕上按 Back 将带我们回到 B。
为了解决这个问题,我们可以保留一个活动堆栈(Arraylist),当按下 home 时,我们必须杀死这个堆栈中的所有活动。
可以通过在 SharedPreferences 或 Application Activity 中管理标志来实现。
在应用程序启动时(在启动画面上)设置标志 = false;在 Logout Click 事件中,只需将标志设置为 true,然后在每个活动的 OnResume() 中,检查标志是否为 true,然后调用 finish()。
它就像一个魅力:)
单击注销时,您可以调用它
private void GoToPreviousActivity() {
setResult(REQUEST_CODE_LOGOUT);
this.finish();
}
上一个活动的 onActivityResult() 再次调用上面的代码,直到完成所有活动。
一种选择是让每个活动的 onCreate 检查登录状态,如果未登录,则使用 finish()。我不喜欢这个选项,因为后退按钮仍然可以使用,在活动自行关闭时导航回来。
您要做的是在 onStop() 或 onPause() 方法上调用 logout() 和 finish()。这将强制 Android 在 Activity 重新启动时调用 onCreate(),因为它不会再将它放在其 Activity 的堆栈中。然后照你说的做,在 onCreate() 中检查登录状态,如果未登录则转发到登录屏幕。
您可以做的另一件事是在 onResume() 中检查登录状态,如果未登录,则完成()并启动登录活动。
不定期副业成功案例分享