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

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

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

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

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

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

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

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

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

Ответ 1

Я могу предложить вам другой подход IMHO более надежный. В основном вам необходимо транслировать сообщение о выходе на все ваши действия, необходимые для пребывания в состоянии входа в систему. Таким образом, вы можете использовать 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 **//
}

Ответ 2

Кажется, обряд посвящения, что новый 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 в этом потоке - лучшее решение (по крайней мере, моему скромному глазу):

fooobar.com/questions/28146/...

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

Удачи.

Ответ 3

UPDATE

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

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

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

Предположим, что 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();
}

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

Ответ 4

Если вы используете API 11 или выше, вы можете попробовать это: FLAG_ACTIVITY_CLEAR_TASK - похоже, вы точно решаете проблему, с которой вы сталкиваетесь. Очевидно, что толпе pre-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();

Ответ 5

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

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

Intent intent = new Intent(activity, SignInActivity.class)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
this.finish();

Ответ 7

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

Ответ 8

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

Ответ 9

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

В моей учетной записи 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 это делается, если я нажму кнопку Back от MainActivity, функция LoginActivity немедленно будет скрыта. На Honeycomb и позже я просто закончу LoginActivity после обработки логина и он правильно воссоздан после обработки выхода.

Ответ 10

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

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

Ответ 11

Когда-то finish() не работает

Я решил эту проблему с помощью

finishAffinity()

Ответ 12

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

@Override
public void onBackPressed() {
    // disable going back to the MainActivity
    moveTaskToBack(true);
}

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

Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
finish();

Все предложения приветствуются.

Ответ 13

Начните работу с 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);

}

Ответ 14

при нажатии Logout вы можете назвать это

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

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

Ответ 15

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

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

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

Ответ 16

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

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

Он работает как шарм:)

Ответ 17

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

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

Ответ 18

            Utility.clearAllSharedPreferences(mContext) //clearing the session
            val intent = Intent(mContext, WelcomeScreen::class.java)
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) //for clear all the activities
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) //for clear all the activities
            startActivity(intent)
            finish()

Ответ 19

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

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

Еще одна вещь, которую вы можете сделать, - проверить статус в журнале onResume(), а если не войти в систему, закончить() и запустить активность входа.