Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

On logout, clear Activity history stack, preventing "back" button from opening logged-in-only Activities

People also ask

How do I start activity and clear back stack?

Declare Activity A as SingleTop by using [android:launchMode="singleTop"] in Android manifest. Now add the following flags while launching A from anywhere. It will clear the stack.

How do I delete all previous activities?

For API 11+ you can use Intent. FLAG_ACTIVITY_CLEAR_TASK|Intent. FLAG_ACTIVITY_NEW_TASK like this: It will totally clears all previous activity(s) and start new activity.


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:

/** 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 **//
}

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.

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.

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.

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).

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):

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.

Good Luck.


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.

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

ORIGINAL ANSWER

Assume that LoginActivity --> HomeActivity --> ... --> SettingsActivity call 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. :)


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.

(Also note: to use this you have to pass in 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();

I spent a few hours on this too ... and agree that FLAG_ACTIVITY_CLEAR_TOP sounds 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 Repass mentioned, 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.

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. Then add the FLAG_ACTIVITY_CLEAR_TOP flag.

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.

Here's my logout function; the View parameter is the button to which the function's attached.

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