Android 注销时,清除活动历史堆栈,防止“后退”按钮打开仅登录的活动
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3007998/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
On logout, clear Activity history stack, preventing "back" button from opening logged-in-only Activities
提问by skyler
All activities in my application require a user to be logged-in to view. Users can log out from almost any activity. This is a requirement of the application. At any point if the user logs-out, I want to send the user to the Login Activity
. At this point I want this activity to be at the bottom of the history stack so that pressing the "back" button returns the user to Android's home screen.
我的应用程序中的所有活动都需要用户登录才能查看。用户几乎可以从任何活动中注销。这是应用程序的要求。在任何时候,如果用户注销,我想将用户发送到 Login Activity
。在这一点上,我希望此活动位于历史堆栈的底部,以便按“后退”按钮将用户返回到 Android 的主屏幕。
I've seen this question asked a few different places, all answered with similar answers (that I outline here), but I want to pose it here to collect feedback.
我已经看到这个问题在几个不同的地方被问到,都得到了类似的答案(我在这里概述了),但我想在这里提出它以收集反馈。
I've tried opening the Login activity by setting its Intent
flags to FLAG_ACTIVITY_CLEAR_TOP
which seems to do as is outlined in the documentation, but does not achieve my goal of placing the Login activity at the bottom of the history stack, and preventing the user from navigating back to previously-seen logged-in activities. I also tried using android:launchMode="singleTop"
for the Login activity in the manifest, but this does not accomplish my goal either (and seems to have no effect anyway).
我已经尝试通过设置其Intent
标志来打开登录活动,FLAG_ACTIVITY_CLEAR_TOP
这似乎与文档中概述的一样,但没有实现我将登录活动置于历史堆栈底部并阻止用户导航回来的目标到以前看到的登录活动。我还尝试android:launchMode="singleTop"
在清单中使用Login 活动,但这也没有实现我的目标(而且似乎没有任何效果)。
I believe I need to either clear the history stack, or finish all previously- opened activities.
我相信我需要清除历史堆栈,或者完成所有以前打开的活动。
One option is to have each activity's onCreate
check logged-in status, and finish()
if not logged-in. I do not like this option, as the back button will still be available for use, navigating back as activities close themselves.
一种选择是让每个活动的onCreate
检查登录状态,finish()
如果没有登录。我不喜欢这个选项,因为后退按钮仍然可以使用,当活动关闭时导航回来。
The next option is to maintain a LinkedList
of references to all open activities that is statically accessible from everywhere (perhaps using weak references). On logout I will access this list and iterate over all previously-opened activities, invoking finish()
on each one. I'll probably begin implementing this method soon.
下一个选项是维护LinkedList
对所有开放活动的引用,这些活动可以从任何地方静态访问(可能使用弱引用)。在注销时,我将访问此列表并遍历所有先前打开的活动,调用finish()
每个活动。我可能很快就会开始实施这种方法。
I'd rather use some Intent
flag trickery to accomplish this, however. I'd be beyond happy to find that I can fulfill my application's requirements without having to use either of the two methods that I've outlined above.
但是,我宁愿使用一些Intent
标志技巧来完成此操作。我非常高兴地发现我可以满足我的应用程序的要求,而不必使用我上面概述的两种方法中的任何一种。
Is there a way to accomplish this by using Intent
or manifest settings, or is my second option, maintaining a LinkedList
of opened activities the best option? Or is there another option that I'm completely overlooking?
有没有办法通过使用Intent
或清单设置来实现这一点,或者我的第二个选择是维护一个LinkedList
打开的活动是最好的选择?或者有没有我完全忽略的另一种选择?
采纳答案by Francesco Laurita
I can suggest you another approach IMHO more robust.
Basically you need to broadcast a logout message to all your Activities needing to stay under a logged-in status. So you can use the sendBroadcast
and install a BroadcastReceiver
in all your Actvities.
Something like this:
我可以建议你另一种方法恕我直言更强大。基本上,您需要向所有需要保持登录状态的活动广播注销消息。因此,您可以在所有活动中使用sendBroadcast
并安装 a BroadcastReceiver
。像这样的东西:
/** on your logout method:**/
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("com.package.ACTION_LOGOUT");
sendBroadcast(broadcastIntent);
The receiver (secured Activity):
接收者(安全活动):
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 **//
}
回答by Mike Repass
It seems a rite of passage that a new Android programmer spends a day researching this issue and reading all of these StackOverflow threads. I am now newly initiated and I leave here trace of my humble experience to help a future pilgrim.
一个新的 Android 程序员花一天时间研究这个问题并阅读所有这些 StackOverflow 线程,这似乎是一种仪式。我现在是新入会的,我在这里留下我卑微经验的痕迹,以帮助未来的朝圣者。
First, there is no obvious or immediate way to do this per my research (as of September 2012).
You'd think you could simple startActivity(new Intent(this, LoginActivity.class), CLEAR_STACK)
but no.
首先,根据我的研究,没有明显或直接的方法可以做到这一点(as of September 2012).
你认为你可以简单startActivity(new Intent(this, LoginActivity.class), CLEAR_STACK)
但没有。
You CAN do startActivity(new Intent(this, LoginActivity.class))
with FLAG_ACTIVITY_CLEAR_TOP
- and this will cause the framework to search down the stack, find your earlier original instance of LoginActivity, recreate it and clear the rest of the (upwards) stack. And since Login is presumably at the bottom of the stack, you now have an empty stack and the Back button just exits the application.
你可以做startActivity(new Intent(this, LoginActivity.class))
用FLAG_ACTIVITY_CLEAR_TOP
-这将导致框架向下搜索堆栈,找到LoginActivity的你早期的原始实例,重新创建和清除(向上)堆栈的其余部分。由于 Login 可能位于堆栈的底部,您现在有一个空堆栈,而 Back 按钮只是退出应用程序。
BUT - this only works if you previously left that original instance of LoginActivity alive at the base of your stack. If, like many programmers, you chose to finish()
that LoginActivity
once the user has successfully logged in, then it's no longer on the base of the stack and the FLAG_ACTIVITY_CLEAR_TOP
semantics do not apply ... you end up creating a new LoginActivity
on top of the existing stack. Which is almost certainly NOT what you want (weird behavior where the user can 'back' their way out of login into a previous screen).
但是 - 这仅在您之前将 LoginActivity 的原始实例留在堆栈底部时才有效。如果像许多程序员一样finish()
,LoginActivity
一旦用户成功登录,您就选择了它,那么它不再位于堆栈的基础上并且FLAG_ACTIVITY_CLEAR_TOP
语义不适用……您最终会LoginActivity
在现有堆栈的顶部创建一个新堆栈。这几乎肯定不是您想要的(用户可以“退出”登录前一个屏幕的奇怪行为)。
So if you have previously finish()
'd the LoginActivity
, you need to pursue some mechanism for clearing your stack and then starting a new LoginActivity
. It seems like the answer by @doreamon
in this thread is the best solution (at least to my humble eye):
因此,如果您以前使用finish()
过LoginActivity
,则需要采用某种机制来清除堆栈然后开始新的LoginActivity
. @doreamon
这个帖子中的答案似乎是最好的解决方案(至少在我卑微的眼中):
https://stackoverflow.com/a/9580057/614880
https://stackoverflow.com/a/9580057/614880
I strongly suspect that the tricky implications of whether you leave LoginActivity alive are causing a lot of this confusion.
我强烈怀疑您是否让 LoginActivity 活着的棘手影响会导致很多这种混乱。
Good Luck.
祝你好运。
回答by thanhbinh84
UPDATE
更新
the super finishAffinity()
method will help to reduce the code but achieve the same. It will finish the current activity as well as all activities in the stack, use getActivity().finishAffinity()
if you are in a fragment.
superfinishAffinity()
方法将有助于减少代码但实现相同的目标。它将完成当前活动以及堆栈中的所有活动,getActivity().finishAffinity()
如果您在片段中,请使用。
finishAffinity();
startActivity(new Intent(mActivity, LoginActivity.class));
ORIGINAL ANSWER
原答案
Assume that LoginActivity --> HomeActivity --> ... --> SettingsActivity call signOut():
假设 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();
}
This works for me, hope that it is helpful for you too. :)
这对我有用,希望它对你也有帮助。:)
回答by xbakesx
If you are using API 11 or higher you can try this: FLAG_ACTIVITY_CLEAR_TASK
--it seems to be addressing exactly the issue you're having. Obviously the pre-API 11 crowd would have to use some combination of having all activities check an extra, as @doreamon suggests, or some other trickery.
如果您使用的是 API 11 或更高版本,您可以尝试以下操作:FLAG_ACTIVITY_CLEAR_TASK
--它似乎正在解决您遇到的问题。显然,API 11 之前的人群必须使用某种组合,让所有活动都检查额外的,如@doreamon 建议的那样,或者其他一些技巧。
(Also note: to use this you have to pass in FLAG_ACTIVITY_NEW_TASK
)
(另请注意:要使用它,您必须传入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();
回答by christinac
I spent a few hours on this too ... and agree that FLAG_ACTIVITY_CLEAR_TOPsounds like what you'd want: clear the entire stack, except for the activity being launched, so the Back button exits the application. Yet as Mike Repassmentioned, FLAG_ACTIVITY_CLEAR_TOP only works when the activity you're launching is already in the stack; when the activity's not there, the flag doesn't do anything.
我也花了几个小时...并同意FLAG_ACTIVITY_CLEAR_TOP听起来像你想要的:清除整个堆栈,除了正在启动的活动,所以后退按钮退出应用程序。然而正如Mike Repass 所提到的,FLAG_ACTIVITY_CLEAR_TOP仅在您启动的活动已经在堆栈中时才有效;当活动不存在时,标志不会做任何事情。
What to do? Put the activity being launching in the stack with FLAG_ACTIVITY_NEW_TASK, which makes that activity the start of a new task on the history stack. Thenadd the FLAG_ACTIVITY_CLEAR_TOP flag.
该怎么办?使用FLAG_ACTIVITY_NEW_TASK将正在启动的 Activity 放入堆栈中,这使得该 Activity 成为历史堆栈上新任务的开始。然后添加 FLAG_ACTIVITY_CLEAR_TOP 标志。
Now, when FLAG_ACTIVITY_CLEAR_TOP goes to find the new activity in the stack, it'll be there and be pulled up before everything else is cleared.
现在,当 FLAG_ACTIVITY_CLEAR_TOP 去寻找堆栈中的新活动时,它会在那里并在其他所有内容被清除之前被拉起。
Here's my logout function; the View parameter is the button to which the function's attached.
这是我的注销功能;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();
}
回答by Gulshan
Lots of answers. May be this one will also help-
很多答案。可能这个也有帮助-
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 version-
Kotlin 版本-
Intent(this, SignInActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}.also { startActivity(it) }
finish()
回答by Yekmer Simsek
Accepted solution is not correct, it has problems as using a broadcast receiver is not a good idea for this problem. If your activity has already called onDestroy() method, you will not get receiver. Best solution is having a boolean value on your shared preferences, and checking it in your activty's onCreate() method. If it should not be called when user is not logged in, then finish activity. Here is sample code for that. So simple and works for every condition.
接受的解决方案是不正确的,它有问题,因为使用广播接收器对于这个问题不是一个好主意。如果您的活动已经调用了 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;
}
回答by Mohamed Ibrahim
Use this it should be helpful to you. Slightly modified xbakesx answer.
使用这个它应该对你有帮助。稍微修改 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);
回答by AJay
Sometime finish()
not working
有时finish()
不工作
I have solved that issue with
我已经解决了这个问题
finishAffinity()
finishAffinity()
回答by Nil
I'd suggest a different approach to this question. Maybe it's not the most efficient one, but I think it's the easiest to apply and requires very little code. Writing the next code in your first activity (log in activity, in my case) won't let the user go back to previously launched activities after logging out.
对于这个问题,我建议采用不同的方法。也许它不是最有效的,但我认为它是最容易应用的,并且只需要很少的代码。在您的第一个活动(在我的情况下为登录活动)中编写下一个代码不会让用户在注销后返回到之前启动的活动。
@Override
public void onBackPressed() {
// disable going back to the MainActivity
moveTaskToBack(true);
}
I'm assuming that LoginActivity is finished just after the user logs in, so that he can't go back to it later by pressing the back button. Instead, the user must press a log out button inside the app in order to log out properly. What this log out button would implement is a simple intent as follows:
我假设 LoginActivity 在用户登录后就完成了,以便他以后无法通过按后退按钮返回到它。相反,用户必须按下应用程序内的注销按钮才能正确注销。这个注销按钮将实现一个简单的意图,如下所示:
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
finish();
All suggestions are welcome.
欢迎提出所有建议。