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

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

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

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

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

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

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

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

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

18 ответов


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

/** 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 проводит день, исследуя эту проблему и читая все эти потоки StackOverflow. Теперь я вновь посвящен, и я оставляю здесь след моего скромного опыта, чтобы помочь будущему паломнику.

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

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

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

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

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

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

Удачи.


обновление

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

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

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

предположим, что LoginActivity --> HomeActivity --> ... --> Вызовов соответствии файле 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();
}

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


Если вы используете 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();

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

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

принятое решение неверно, у него есть проблемы, поскольку использование широковещательного приемника не является хорошей идеей для этой проблемы. Если ваша активность уже вызвала метод onDestroy (), вы не получите receiver. Лучшее решение - иметь логическое значение в ваших общих настройках и проверять его в методе onCreate() вашего activty. Если он не должен вызываться, когда пользователь не вошел в систему, завершите действие. Вот пример кода для этого. Так просто и работает для каждого условия.

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

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

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

    Intent i = new Intent(SettingsActivity.this, LoginActivity.class);
    i.addFlags(IntentCompat.FLAG_ACTIVITY_CLEAR_TASK
            | Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(i);

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

В моем 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 после обработки логина, и он правильно воссоздается после обработки выхода из системы.


когда-нибудь finish() не работает

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

finishAffinity()


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

}

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

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

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


это возможно путем управления флагом в SharedPreferences или в деятельности приложения.

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

Это работает как шарм :)


при щелчке выхода из системы вы можете назвать это

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

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


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

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

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

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

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

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

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


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

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

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