Очистить весь стек истории и начать новое действие на Android
Можно ли начать действие в стеке, очистив всю историю до него?
Ситуация
У меня есть стек активности, который идет либо A->B->C, либо B->C (экран A выбирает токен пользователя, но у многих пользователей только один токен).
На экране C пользователь может выполнить действие, которое делает экран B недействительным, поэтому приложение хочет перевести их на экран A независимо от того, находится ли он уже в стеке. Экран A должен быть единственным элементом в стеке в моем приложении.
Заметки
Есть много других подобных вопросов, но я не нашел ничего, что отвечало бы на этот точный вопрос. Я пробовал звонить getParent().finish()
- это всегда приводит к исключению нулевого указателя. FLAG_ACTIVITY_CLEAR_TOP
работает только если активность уже в стеке.
14 ответов
На уровне API 11 новый флаг намерений был добавлен только для этого: Intent.FLAG_ACTIVITY_CLEAR_TASK
Просто чтобы уточнить, используйте это:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
К сожалению для API lvl <= 10, я еще не нашел чистого решения для этого. Решение "DontHackAndroidLikeThis" действительно является чистой взломом. Вы не должны этого делать.:)
Изменить: Согласно комментарию @ Ben Pearson, для API <=10 теперь можно использовать класс IntentCompat для того же. Можно использовать IntentCompat.FLAG_ACTIVITY_CLEAR_TASK
флаг для очистки задачи. Таким образом, вы также можете поддерживать предварительный уровень API 11.
Случай 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 очищает стек и делает его верхним. Так что, когда мы нажимаем кнопку "назад", все приложение будет завершено.
С более новой версией Android>= использование API 16finishAffinity()
подход подходит для>= API 16.
Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
- Это то же самое, что запуск нового действия и очистка всего стека.
- ИЛИ Перезапустите MainActivity/FirstActivity.
Я тоже потратил на это несколько часов... и согласен с тем, что 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();
}
Вы не должны менять стек. Кнопка возврата 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()
из стека.
Еще раз, вы не должны делать это:)
Сразу после начала нового действия, используя startActivity
обязательно позвони finish()
так, чтобы текущая активность не складывалась за новой.
Продвинутый многоразовый котлин:
Вы можете установить флаг напрямую, используя метод setter. В Котлине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
}
Попробуйте код ниже,
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);
Попробуй это:
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();
Для меня ни один из вышеперечисленных методов не работает.
Просто сделайте это, чтобы очистить все предыдущие действия:
finishAffinity() // if you are in fragment use activity.finishAffinity()
Intent intent = new Intent(this, DestActivity.class); // with all flags you want
startActivity(intent)
Intent i = new Intent(MainPoliticalLogin.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
В Яве: -
startActivity(new Intent(getApplicationContext(),ChooseServiceActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
Иногда ваш эмулятор Android может не подключиться к инструменту eclipse DDMS и запросить запуск adb вручную. В этом случае вы можете запустить или остановить ADB с помощью командной строки.
Я нашел слишком простой взлом просто сделать это добавить новый элемент в AndroidManifest
как:-
<activity android:name=".activityName"
android:label="@string/app_name"
android:noHistory="true"/>
android:noHistory
очистит вашу нежелательную активность от стека.