При выходе из системы очистите стек истории действий, чтобы кнопка «Назад» не открывала вход в систему только для входа. - PullRequest
225 голосов
/ 09 июня 2010

Все действия в моем приложении требуют, чтобы пользователь был авторизован для просмотра.Пользователи могут выйти практически из любой активности.Это требование приложения.В любой момент, если пользователь выходит из системы, я хочу отправить пользователя на логин Activity.На данный момент я хочу, чтобы это действие было в нижней части стека истории, чтобы при нажатии кнопки «назад» пользователь возвращался на домашний экран Android.

Я видел этот вопрос, заданный в нескольких разных местах,все ответили схожими ответами (которые я обрисовал здесь), но я хочу изложить их здесь для сбора отзывов.

Я попытался открыть действие входа в систему, установив для его флагов Intent значение FLAG_ACTIVITY_CLEAR_TOP, что выглядитсделать, как указано в документации, но не достиг своей цели - поместить действие «Вход в систему» ​​внизу стека истории и не дать пользователю перейти обратно к ранее просмотренным действиям, вошедшим в систему.Я также попытался использовать android:launchMode="singleTop" для входа в систему в манифесте, но это также не решает мою задачу (и, похоже, не дает никакого эффекта).

Мне кажется, мне нужно либо очистить стек истории,или завершите все ранее открытые действия.

Один из вариантов - иметь для каждого действия onCreate проверку статуса входа в систему и finish(), если не вошел в систему.Мне не нравится эта опция, поскольку кнопка «Назад» будет по-прежнему доступна для использования, переходя назад, когда действия закрываются.

Следующий вариант - сохранить LinkedList ссылок на все открытые действия, которые являются статически.доступны везде (возможно, с использованием слабых ссылок).Выйдя из системы, я получу доступ к этому списку и переберу все ранее открытые действия, вызывая finish() для каждого из них.Я, вероятно, скоро начну реализовывать этот метод.

Однако я бы предпочел использовать некоторую хитрость для флага Intent для достижения этой цели.Я был бы очень рад обнаружить, что могу выполнить требования своего приложения, не используя ни один из двух методов, которые я описал выше.

Есть ли способ сделать это с помощью Intent илинастройки манифеста, или мой второй вариант, поддержание LinkedList открытых действий - лучший вариант?Или есть другой вариант, который я полностью пропускаю?

Ответы [ 18 ]

207 голосов
/ 09 июня 2010

Я могу предложить вам другой подход, ИМХО, более надежный. По сути, вам необходимо передать сообщение о выходе всем вашим действиям, которым необходимо оставаться в состоянии входа в систему. Таким образом, вы можете использовать sendBroadcast и установить BroadcastReceiver во всех ваших Actvities. Примерно так:

/** 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 **//
}
147 голосов
/ 18 сентября 2012

Похоже, что новый программист на 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, предположительно, находится в нижней части стека, у вас теперь есть пустой стек, и кнопка «Назад» просто выходит из приложения.

НО - это работает, только если вы ранее оставили этот оригинальный экземпляр LoginActivity живым наоснова вашего стека.Если, как и многие программисты, вы выбрали finish(), что LoginActivity после того, как пользователь успешно вошел в систему, то это больше не основано на стеке, и семантика FLAG_ACTIVITY_CLEAR_TOP не применяется ... вы в конечном итоге создаетеновый LoginActivity поверх существующего стека.Это почти наверняка НЕ ​​то, что вы хотите (странное поведение, когда пользователь может «вернуться» из своего входа в систему на предыдущем экране).

Так что, если вы ранее finish() 'd LoginActivity,вам нужно использовать какой-то механизм для очистки стека и запуска нового LoginActivity.Кажется, что ответ @doreamon в этой теме является лучшим решением (по крайней мере, на мой смиренный взгляд):

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

Я сильно подозреваю, что хитрые последствия того,оставьте LoginActivity в живых, это вызывает много путаницы.

Удачи.

109 голосов
/ 06 марта 2012

UPDATE

метод super finishAffinity() поможет сократить код, но достичь того же. Он завершит текущее действие, а также все действия в стеке, используйте getActivity().finishAffinity(), если вы находитесь во фрагменте.

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

ОРИГИНАЛЬНЫЙ ОТВЕТ

Предположим, что LoginActivity -> HomeActivity -> ... -> Настройки CallAutOut ():

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

Это работает для меня, надеюсь, что это будет полезно и для вас. :)

69 голосов
/ 05 февраля 2013

Если вы используете 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();
30 голосов
/ 19 июня 2013

Я тоже потратил на это несколько часов ... и согласен с тем, что FLAG_ACTIVITY_CLEAR_TOP звучит так, как вам бы хотелось: очистить весь стек, кроме запускаемого действия, поэтому кнопка Назад выходит изприложение.Тем не менее, как упоминалось 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();
}
6 голосов
/ 22 июля 2015

много ответов.Может быть, этот тоже поможет -

Intent intent = new Intent(activity, SignInActivity.class)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
this.finish();
3 голосов
/ 09 июля 2015

Принятое решение не является правильным, оно имеет проблемы, так как использование широковещательного приемника не является хорошей идеей для этой проблемы. Если ваша деятельность уже вызвала метод 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;
}
3 голосов
/ 25 июня 2014

Используйте это, это должно быть полезно для вас. Слегка измененный ответ 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);
1 голос
/ 18 декабря 2014

Выбранный ответ умный и хитрый.Вот как я это сделал:

LoginActivity - это корневая активность задачи, установите для нее android: noHistory = "true" в Manifest.xml;Скажем, вы хотите выйти из SettingsActivity, вы можете сделать это, как показано ниже:

    Intent i = new Intent(SettingsActivity.this, LoginActivity.class);
    i.addFlags(IntentCompat.FLAG_ACTIVITY_CLEAR_TASK
            | Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(i);
1 голос
/ 05 ноября 2013

Вот решение, которое я нашел в своем приложении.

В моей функции 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);
}

Затем в методе onActivityForResult моего LoginActivity:

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

Наконец, после обработки выхода из любого другого действия:Кнопка от MainActivity, LoginActivity сразу скрывается.В Honeycomb и более поздних версиях я просто завершаю LoginActivity после обработки имени входа, и оно правильно воссоздается после обработки выхода из системы.

...