Очистить весь стек истории и начать новое действие на Android


332

Можно ли начать действие в стеке, очистив всю историю до него?

Ситуация

У меня есть стек активности, который идет либо A-> B-> C, либо B-> C (экран A выбирает токен пользователя, но у многих пользователей только один токен).

На экране C пользователь может выполнить действие, которое делает экран B недействительным, поэтому приложение хочет перевести их на экран A, независимо от того, находится ли он уже в стеке. Экран А должен быть единственным элементом в стеке в моем приложении.

Ноты

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

Ответы:


658

На уровне API 11 новый флаг намерений был добавлен только для этого: Intent.FLAG_ACTIVITY_CLEAR_TASK

Просто чтобы уточнить, используйте это:

Ява

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

Котлин

intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

К сожалению для API lvl <= 10, я еще не нашел чистого решения для этого. Решение "DontHackAndroidLikeThis" действительно является чистой взломом. Ты не должен этого делать. :)

Изменить: Согласно комментарию @ Бен Пирсон , для API <= 10 теперь можно использовать класс IntentCompat для того же. Можно использовать IntentCompat.FLAG_ACTIVITY_CLEAR_TASKфлаг, чтобы очистить задачу. Таким образом, вы также можете поддерживать предварительный уровень API 11.


23
Просто чтобы уточнить, используйте это: intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
user123321

2
без Intent.FLAG_ACTIVITY_NEW_TASK приложение иногда просто закрывается на
андроиде

22
IntentCompat теперь также имеет флаг для очистки задачи, так что вы можете поддерживать предварительный уровень API 11 - developer.android.com/reference/android/support/v4/content/…
Бен Пирсон

10
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK игнорируется на устройствах с уровнем API <10. developer.android.com/reference/android/support/v4/content/…
Дэвид

7
Флаг IntentCompat только для того, чтобы избежать сбоя, но ничего не делает, как говорит @David.
Слой

49

Случай 1: только два вида деятельности A и B:

Здесь поток действий - A-> B.. При нажатии на кнопку «Назад» на кнопке B нам нужно закрыть приложение, а затем, при запуске действия B из A, просто вызвать метод finish (), и это не позволит Android сохранить действие A в Backstack.eg для действия A. Loding / Splash экран приложения.

Intent newIntent = new Intent(A.this, B.class);
startActivity(newIntent);
finish();

Случай 2: Более двух видов деятельности:

Если есть поток, такой как A-> B-> C-> D-> B и при нажатии кнопки «Назад» в Деятельности B при переходе из Деятельности D. В этом случае мы должны использовать.

Intent newIntent = new Intent(D.this,B.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent);

Здесь действие B будет запускаться из заднего стека, а не из нового экземпляра из-за Intent.FLAG_ACTIVITY_CLEAR_TOP и Intent.FLAG_ACTIVITY_NEW_TASK очищает стек и делает его верхним. Так что, когда мы нажимаем кнопку «назад», все приложение будет завершено.


2
Это сработало для меня. Я ставлю во ВСЕХ действиях эти флаги. В этих действиях кнопки «назад» работают идеально, переходя к предыдущему действию, а в основной операции с намерением намерения = новое намерение (Intent.ACTION_MAIN); intent.addCategory (Intent.CATEGORY_HOME); intent.addFlags (Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); startActivity (намерение); финиш(); Все приложение закрыто, все еще в памяти, но не активно, и если вы перезапустите приложение, оно перейдет на заставку :)
Rako

Это должен быть лучший ответ. Если у кого-то есть такой же сценарий со мной: A-> B-> C-> D-> E-> (B) Из E-> B должен быть результат: A-> B
Алексис Чавес

39

С более новой версией Android> = использование API 16 finishAffinity()

подход подходит для> = API 16.

Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
  • Это то же самое, что начать новую активность и очистить весь стек.
  • ИЛИ Перезапустите MainActivity / FirstActivity.

1
Это сработало, флаги не работали на 4.xx для меня, и это сработало отлично! Спасибо
Джонатан Аст,

1
Кажется, это правильный ответ, если ваша цель - завершить все действия, указанные ниже, включая текущее действие, и начать новое задание в своей задаче.
ToBe

24

Я тоже потратил на это несколько часов ... и согласен с тем, что 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();
}

1
Вы имеете в виду CLEAR_TASK вместо CLEAR_TOP?
Энди

14

Вы не должны менять стек. Кнопка возврата Android должна работать как в веб-браузере.

Я могу придумать, как это сделать, но это довольно круто.

  • Сделайте свою деятельность singleTask, добавив ее в AndroidManifest пример:

    <activity android:name=".activities.A"
              android:label="@string/A_title"
              android:launchMode="singleTask"/>
    
    <activity android:name=".activities.B"
              android:label="@string/B_title"
              android:launchMode="singleTask"/>
  • Расширьте Applicationкоторый будет держать логику, куда идти.

Пример:

public class DontHackAndroidLikeThis extends Application {

  private Stack<Activity> classes = new Stack<Activity>();

  public Activity getBackActivity() {
    return classes.pop();
  }

  public void addBackActivity(Activity activity) {
    classes.push(activity);
  }
}

От а до б:

DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(A.class); 
startActivity(this, B.class);

От B до C:

DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(B.class); 
startActivity(this, C.class);

В С:

If ( shouldNotGoBackToB() ) {
  DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
  app.pop();
}

и обработайте кнопку возврата pop()из стека.

Еще раз, вы не должны делать это :)


В конце я решаю оставить стек нетронутым и просто сказать пользователю, что его текущий экран был недействительным
Casebash

1
Очень расстраивает тот факт, что Android уже не позволяет нам таким образом управлять стеком активности. Я хотел бы использовать это решение в своих будущих приложениях для Android.
Cephron

4
Просто чтобы понять, почему это не следует использовать: это хороший способ создать утечки памяти. В какой-то момент ОС может принять решение об удалении фоновых операций, но, поскольку Applicationпроисходит их получение, ОС не сможет освободить память, оставшуюся от уничтоженных операций.
Вит Худенко

@ Архимед Есть ли другие проблемы? Утечка памяти может быть устранена путем сохранения только слабых ссылок.
Навин

1
@Navin да, утечек можно избежать с помощью слабых ссылок, но если после GC не будет живой ссылки на активность, тогда весь подход бесполезен. Еще раз - не делайте этого, это неправильный подход для Android.
Вит Худенко

12

Сразу после запуска нового действия с помощью startActivityубедитесь, что вы вызвали его, finish()чтобы текущее действие не укладывалось за новым.


+1 Хорошее решение, чтобы предотвратить попадание в стек истории только одного действия в определенной ситуации.
Marsbear

27
не работает, если у вас в стеке более одного действия, финиш просто очистит предыдущее действие, но не остальные ....
Necronet

5

Попробуй это:

Intent logout_intent = new Intent(DashboardActivity.this, LoginActivity.class);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(logout_intent);
finish();

4

Продвинутый многоразовый котлин:

Вы можете установить флаг напрямую, используя метод установки. В Kotlin orесть замена для битовой Java или |.

intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK

Если вы планируете использовать это регулярно, создайте функцию расширения Intent

fun Intent.clearStack() {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

Затем вы можете напрямую вызвать эту функцию перед началом

intent.clearStack()

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

fun Intent.clearStack(additionalFlags: Int = 0) {
    flags = additionalFlags or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

2
Intent i = new Intent(MainPoliticalLogin.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);

2

Попробуйте приведенный ниже код,

Intent intent = new Intent(ManageProfileActivity.this, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|
                Intent.FLAG_ACTIVITY_CLEAR_TASK| 
                Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

если я использую, как это действие обновляется еще раз, вызовите API, но ранее существующий все statck очищается
Harsha

2

Для меня ни один из вышеперечисленных методов не работает.

Просто сделайте это, чтобы очистить все предыдущие действия :

finishAffinity() // if you are in fragment use activity.finishAffinity()
Intent intent = new Intent(this, DestActivity.class); // with all flags you want
startActivity(intent)

-1

Иногда ваш эмулятор Android может не подключиться к инструменту eclipse DDMS и запросить запуск adb вручную. В этом случае вы можете запустить или остановить ADB с помощью командной строки.


1
Иногда ваш эмулятор Android может не подключиться к инструменту eclipse DDMS и запросить запуск adb вручную. В этом случае вы можете запустить или остановить ADB с помощью командной строки. Intent i = new Intent (OldActivity.this, NewActivity.class); // установить новую задачу и снять флажки i.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) startActivity (i);
RajeshkumarG

-2

Я нашел слишком простой взлом просто сделать это добавить новый элемент в AndroidManifestвиде: -

<activity android:name=".activityName"
          android:label="@string/app_name"
          android:noHistory="true"/>

The android:noHistoryочистит вашу нежелательную деятельность от стека.


2
Этот подход может вызвать проблемы на Android 6.0+, если вы попросите разрешения в этом действии.
Виталий А
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.