При выходе из системы очистите стек истории действий, чтобы кнопка "Назад" не открывала вход в систему только для входа.

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

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

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

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

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

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

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

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

18 ответов

Решение

Я могу предложить вам другой подход, ИМХО более надежный. По сути, вам необходимо передать сообщение о выходе из системы для всех ваших действий, которым необходимо оставаться в состоянии входа в систему. Таким образом, вы можете использовать sendBroadcast и установить 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 проводит целый день, исследуя эту проблему и читая все эти потоки Stackru. Сейчас я новичок и оставляю здесь след своего скромного опыта, чтобы помочь будущему паломнику.

Во-первых, в моем исследовании нет очевидного или немедленного способа сделать это. (as of September 2012). Вы думаете, вы могли бы просто startActivity(new Intent(this, LoginActivity.class), CLEAR_STACK) но нет

Ты можешь сделать startActivity(new Intent(this, LoginActivity.class)) с FLAG_ACTIVITY_CLEAR_TOP - и это заставит платформу выполнить поиск вниз по стеку, найти ваш предыдущий оригинальный экземпляр LoginActivity, воссоздать его и очистить оставшуюся часть (вверх) стека. А поскольку логин находится, вероятно, в нижней части стека, у вас теперь есть пустой стек, а кнопка "Назад" просто выходит из приложения.

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

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

/questions/47987207/pri-vyihode-iz-sistemyi-ochistite-stek-istorii-dejstvij-chtobyi-knopka-nazad-ne-otkryivala-vhod-v-sistemu-tolko-dlya-vhoda/47987345#47987345

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

Удачи.

ОБНОВИТЬ

супер 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();
}

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

Если вы используете 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 звучит так, как вам бы хотелось: очистить весь стек, кроме запускаемого действия, чтобы кнопка "Назад" выходила из приложения. Тем не менее, как 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();

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

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

Когда-то 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);
}

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

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 - это корневая активность задачи, установите для нее 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);

Это сработало для меня:

     // 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);

По щелчку выхода вы можете позвонить

private void GoToPreviousActivity() {
    setResult(REQUEST_CODE_LOGOUT);
    this.finish();
}

onActivityResult() предыдущей операции, вызывайте этот код еще раз, пока вы не завершите все действия.

Начните свою деятельность с 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);

}

Это возможно путем управления флагом в SharedPreferences или в Application Activity.

При запуске приложения (на заставке) установите флаг = false; При событии "Выйти из системы" просто установите флажок "истина" и в OnResume() каждой операции проверьте, установлен ли флажок "истина", затем вызовите finish().

Отлично работает:)

Предоставленное решение @doreamon отлично работает во всех случаях, кроме одного:

Если после входа в систему пользователь Killing Login перешел прямо к среднему экрану. Например, в потоке A->B->C, перейдите, как: Логин -> B -> C -> Нажмите ярлык для дома. Использование FLAG_ACTIVITY_CLEAR_TOP очищает только действие C, так как Home(A) отсутствует в истории стека. Нажатие Back на экране приведет нас обратно к B.

Чтобы решить эту проблему, мы можем сохранить стек действий (Arraylist), и когда нажата кнопка home, мы должны убить все действия в этом стеке.

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

То, что вы хотите сделать, это вызвать logout () и finish () для ваших методов onStop () или onPause (). Это заставит Android вызывать onCreate () при возобновлении активности, поскольку он больше не будет находиться в стеке своей активности. Затем сделайте, как вы говорите, в onCreate () проверьте статус вошедшего в систему и перешли на экран входа в систему, если не вошли в систему.

Другая вещь, которую вы могли бы сделать, это проверить статус входа в систему в onResume (), и, если не вошли в систему, finish () и запустить действие входа в систему.

Другие вопросы по тегам