Очистіть весь стек історії та почніть нову діяльність на Android


332

Чи можна розпочати діяльність на стеці, очистивши всю історію перед цим?

Ситуація

У мене є стек активності, який переходить або A-> B-> C, або B-> C (екран A вибирає маркер користувачів, але багато користувачів мають лише один маркер).

На екрані C користувач може вчинити дію, яка робить екран B недійсним, тому програма хоче перенести їх на екран A, незалежно від того, чи він уже є у стеку. Екран А повинен бути єдиним елементом у стеці в моєму додатку.

Примітки

Є багато інших подібних питань, але я не знайшов нічого, що відповіло б на це точне запитання. Я спробував зателефонувати getParent().finish()- це завжди призводить до нульового виключення вказівника. FLAG_ACTIVITY_CLEAR_TOPпрацює лише в тому випадку, якщо активність уже є в стеку.

Відповіді:


658

На рівні 11 API був доданий новий прапор намірів саме для цього: Намір.FLAG_ACTIVITY_CLEAR_TASK

Просто для уточнення використовуйте це:

Java

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 також.


23
Просто для уточнення використовуйте це: intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
користувач123321

2
без наміру.FLAG_ACTIVITY_NEW_TASK додаток іноді просто закривається на android 4
max4ever

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.
Sloy

49

Випадок 1: Лише два види діяльності A і B:

Тут потік активності - A-> B. Якщо натиснути кнопку B з клавіші B, нам потрібно закрити додаток, тоді, починаючи активність B з просто закінчення виклику (), це не дозволить андроїд зберігати активність A у Backstack.eg для активності A Екран розміщення / сплеск програми.

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

Випадок 2: Більше двох заходів:

Якщо є потік на зразок A-> B-> C-> D-> B і натискання кнопки "назад" у "B", що надходить з Activity 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 буде запущена з задніх стек, а не з нового примірника через наміри.


2
Це працювало для мене. Я виставляю ВСІ види діяльності ці прапори. У цих заходах кнопки "назад" працюють ідеально, переходячи до попередньої діяльності, а в основній діяльності з наміром = новий намір (Intent.ACTION_MAIN); intent.addCategory (Intent.CATEGORY_HOME); intent.addFlags (Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); startActivity (наміри); закінчити (); Весь додаток закрито, все ще зберігається в пам'яті, але немає активного, і якщо перезапустити додаток, перейде на екран сплеску :)
Рако

Це має бути найкращою відповіддю. Якщо у кого сценарій однаковий зі мною: A-> B-> C-> D-> E -> (B) Від E-> B повинен мати результат: A-> B
Alexis Chavez

39

З новітньою версією Android> = API 16 finishAffinity()

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

Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
  • Це те саме, що запустити новий Activity, і очистити весь стек.
  • АБО Перезапустіть до 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.
Сефрон

4
Просто, щоб зрозуміти, чому цього не слід використовувати: це приємний спосіб створити витоки пам'яті. У якийсь момент ОС може вирішити знищити фонову діяльність, але оскільки Applicationзаймає їх екземпляри, ОС не зможе звільнити цю оперативну пам’ять від знищеної діяльності.
Віт Худенко

@Arhimed Чи є інші проблеми? Витік пам'яті можна виправити, зберігаючи лише слабкі посилання.
Навін

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

Розширений багаторазовий котлін:

Ви можете встановити прапор безпосередньо, використовуючи метод сеттера. У Котліні orце заміна Java побіжно або |.

intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK

Якщо ви плануєте регулярно використовувати це, створіть функцію розширення намірів

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 очищений
Харша

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 може не підключити інструмент DDMS затемнення і попросити запустити adb вручну. У такому випадку ви можете запустити або зупинити adb за допомогою командного рядка.


1
Іноді ваш емулятор для Android може не підключити інструмент DDMS затемнення і попросити запустити adb вручну. У такому випадку ви можете запустити або зупинити adb за допомогою командного рядка. Намір i = новий намір (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"/>

це android:noHistoryочистить вашу небажану активність від Stack.


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