Wyczyść cały stos historii i rozpocznij nową aktywność na Androidzie


332

Czy można rozpocząć działanie na stosie, czyszcząc całą historię przed nim?

Sytuacja

Mam stos aktywności, który albo A-> B-> C lub B-> C (ekran A wybiera token użytkowników, ale wielu użytkowników ma tylko jeden token).

Na ekranie C użytkownik może podjąć działanie, które powoduje, że ekran B jest nieważny, więc aplikacja chce przenieść go na ekran A, niezależnie od tego, czy znajduje się już na stosie. Ekran A powinien być zatem jedynym elementem na stosie w mojej aplikacji.

Notatki

Jest wiele innych podobnych pytań, ale nie znalazłem niczego, co odpowiadałoby dokładnie na to pytanie. Próbowałem zadzwonić getParent().finish()- zawsze powoduje to wyjątek wskaźnika zerowego. FLAG_ACTIVITY_CLEAR_TOPdziała tylko wtedy, gdy aktywność jest już na stosie.

Odpowiedzi:


658

Na poziomie API 11 dodano nową flagę intencji właśnie w tym celu: Intent.FLAG_ACTIVITY_CLEAR_TASK

Aby to wyjaśnić, użyj tego:

Jawa

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


Kotlin

intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK


Niestety dla API lvl <= 10, nie znalazłem jeszcze czystego rozwiązania tego problemu. Rozwiązanie „DontHackAndroidLikeThis” to rzeczywiście czysta hackery. Nie powinieneś tego robić. :)

Edycja: Zgodnie z komentarzem @ Ben Pearson , dla API <= 10 można teraz używać klasy IntentCompat do tego samego. Można użyć IntentCompat.FLAG_ACTIVITY_CLEAR_TASKflagi do wyczyszczenia zadania. Możesz więc obsługiwać także 11-letni interfejs API.


23
Aby to wyjaśnić, użyj tego: intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
— user123321,

2
bez zamiaru.FLAG_ACTIVITY_NEW_TASK aplikacja czasami po prostu zamyka się na
— Androidzie

22
IntentCompat ma również flagę do wyczyszczenia zadania, więc możesz obsługiwać pre API 11 poziomu - developer.android.com/reference/android/support/v4/content/…
— Ben Pearson

10
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK jest ignorowany na urządzeniach z poziomem API <10. developer.android.com/reference/android/support/v4/content/…
— David

7
Flaga IntentCompat ma jedynie na celu uniknięcie awarii, ale nie robi nic, jak mówi @David.
— Sloy

49

Przypadek 1: Tylko dwie czynności A i B:

Tutaj przepływ działań to A-> B.Kliknięcie przycisku B z B musimy zamknąć aplikację, a następnie uruchamiając działanie B z A po prostu wywołaj polecenie zakończenia (), co uniemożliwi Androidowi zapisywanie działania A w Backstack.eg dla działania A jest Loding / Splash ekran aplikacji.

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

Przypadek 2: Więcej niż dwa działania:

Jeśli istnieje przepływ taki jak A-> B-> C-> D-> B i po kliknięciu przycisku Wstecz w działaniu B, gdy pochodzi on z działania D. W takim przypadku powinniśmy użyć.

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

Tutaj działanie B zostanie uruchomione z backstacka, a nie z nowej instancji z powodu Intent.FLAG_ACTIVITY_CLEAR_TOP i Intent.FLAG_ACTIVITY_NEW_TASK usuwa stos i czyni go najwyższym, więc po naciśnięciu przycisku Wstecz cała aplikacja zostanie zakończona.


2
To zadziałało dla mnie. Włączyłem WSZYSTKIE działania do tych flag. W tych działaniach przyciski powrotu działają idealnie, przechodząc do poprzedniej czynności, aw głównej aktywności z intencją intent = nowa intencja (Intent.ACTION_MAIN); intent.addCategory (Intent.CATEGORY_HOME); intent.addFlags (Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); startActivity (cel); koniec(); Cała aplikacja jest zamknięta, wciąż w pamięci, ale nie jest aktywna, a jeśli uruchomisz ponownie, aplikacja przejdzie do ekranu powitalnego :)
— Rako

To powinna być najlepsza odpowiedź. Jeśli ktoś ma ze mną taki sam scenariusz: A-> B-> C-> D-> E -> (B) Od E-> B powinien dać wynik: A-> B
— Shem Alexis Chavez

39

Z nowszą wersją Androida> = użyj interfejsu API 16 finishAffinity()

podejście jest odpowiednie dla> = API 16.

Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
  • Jest to to samo, co rozpoczęcie nowej aktywności i wyczyszczenie całego stosu.
  • LUB Uruchom ponownie do MainActivity / FirstActivity.

1
To załatwiło sprawę, flagi nie działały dla mnie na 4.xx i to działało idealnie! Dzięki
— Jonathan Aste,

1
To wydaje się być prawidłową odpowiedzią, jeśli Twoim celem jest zakończenie wszystkich działań poniżej i włączenie bieżącej aktywności i rozpoczęcie nowej aktywności w ich własnym zadaniu.
— ToBe

24

Spędziłem nad tym także kilka godzin ... i zgadzam się, że FLAG_ACTIVITY_CLEAR_TOP brzmi tak, jak chcesz: wyczyść cały stos, z wyjątkiem uruchomionej aktywności, więc przycisk Wstecz wychodzi z aplikacji. Jednak, jak wspomniał Mike Repass, FLAG_ACTIVITY_CLEAR_TOP działa tylko wtedy, gdy aktywność, którą uruchamiasz, znajduje się już na stosie; gdy działania nie ma, flaga nic nie robi.

Co robić? Umieść uruchamiane działanie w stosie za pomocą FLAG_ACTIVITY_NEW_TASK, co powoduje, że działanie to rozpoczyna nowe zadanie na stosie historii. Następnie dodaj flagę FLAG_ACTIVITY_CLEAR_TOP.

Teraz, gdy FLAG_ACTIVITY_CLEAR_TOP pójdzie znaleźć nową aktywność na stosie, będzie tam i zostanie podniesiona, zanim wszystko inne zostanie wyczyszczone.

Oto moja funkcja wylogowania; parametr View to przycisk, do którego dołączona jest funkcja.

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
masz na myśli CLEAR_TASK zamiast CLEAR_TOP?
— Andy,

14

Nie powinieneś zmieniać stosu. Przycisk Wstecz Androida powinien działać jak w przeglądarce internetowej.

Mogę wymyślić sposób, aby to zrobić, ale to dość hack.

  • Wykonaj swoje działania singleTask, dodając je do AndroidManifest przykładu:

    <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"/>
  • Rozszerz, Applicationktóra będzie zawierać logikę, gdzie iść.

Przykład:

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

Od A do B:

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

Od B do C:

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

W C:

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

i przytrzymaj przycisk powrotu do pop()stosu.

Jeszcze raz nie powinieneś tego robić :)


W końcu postanawiam pozostawić stos nienaruszony i po prostu powiedzieć użytkownikowi, że jego obecny ekran jest nieprawidłowy
— Casebash

1
Bardzo frustrujące jest to, że Android nie pozwala nam już zarządzać stosami aktywności w ten sposób. Kusiłoby mnie, aby użyć tego rozwiązania w moich przyszłych aplikacjach na Androida.
— Cefron

4
Żeby było jasne, dlaczego nie należy tego używać: to dobry sposób na tworzenie wycieków pamięci. W pewnym momencie system operacyjny może zdecydować o zabiciu działań w tle, ale od momentu Applicationwystąpienia ich system operacyjny nie będzie w stanie zwolnić pamięci RAM pozostałej po zniszczonych działaniach.
— Vit Khudenko,

@Arhimed Czy są jeszcze inne problemy? Wyciek pamięci można naprawić, utrzymując tylko słabe referencje.
— Navin

1
@Navin tak, przecieków można uniknąć przy słabych referencjach, ale jeśli po GC nie będzie referencji na żywo, całe podejście jest bezużyteczne. Jeszcze raz - nie rób tego, to jest złe podejście do Androida.
— Vit Khudenko

12

Natychmiast po rozpoczęciu nowej aktywności za pomocą startActivityupewnij się, że dzwonisz, finish()aby bieżąca aktywność nie została ułożona za nową.


+1 Dobre rozwiązanie, aby zapobiec umieszczeniu dokładnie jednego działania w danej sytuacji na stosie historii.
— marsbear

27
nie działa, jeśli masz więcej niż jedną działalność w stosie wykończenie będzie po prostu usunąć poprzednią działalność, ale nie innych ....
— Necronet

5

Spróbuj tego:

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

Zaawansowane wielokrotnego użytku Kotlin:

Możesz ustawić flagę bezpośrednio za pomocą metody setter. W Kotlin orjest zamiennikiem Java bitowej lub |.

intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK

Jeśli planujesz używać tego regularnie, utwórz funkcję rozszerzenia Intent

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

Następnie możesz bezpośrednio wywołać tę funkcję przed rozpoczęciem zamiaru

intent.clearStack()

Jeśli potrzebujesz opcji dodania dodatkowych flag w innych sytuacjach, dodaj opcjonalny parametr do funkcji rozszerzenia.

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

Wypróbuj poniższy kod,

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

jeśli korzystam z tej aktywności, to ponownie zaktualizowałem api, ale wcześniej istniejące wszystkie statck zostały wyczyszczone
— Harsha

2

Dla mnie żadna z powyższych metod nie działa.

Po prostu zrób to, aby wyczyścić wszystkie poprzednie działania :

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

-1

Czasami emulator Androida może nie połączyć się z narzędziem DDMS zaćmienia i poprosić o ręczne uruchomienie adb. W takim przypadku możesz uruchomić lub zatrzymać adb za pomocą wiersza polecenia.


1
Czasami emulator Androida może nie połączyć się z narzędziem DDMS zaćmienia i poprosić o ręczne uruchomienie adb. W takim przypadku możesz uruchomić lub zatrzymać adb za pomocą wiersza polecenia. Intent i = new Intent (OldActivity.this, NewActivity.class); // ustaw nowe zadanie i wyczyść flagi i.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) startActivity (i);
— RajeshkumarG

-2

Znalazłem zbyt prosty hack, po prostu dodaj ten nowy element AndroidManifestjako:

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

android:noHistoryskasuje niechcianych aktywności ze stosu.


2
To podejście może powodować problemy w Androidzie 6.0+, jeśli poprosisz o uprawnienia w tym działaniu.
— Witalij A
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.