Po pierwsze wiem, że tak naprawdę nie należy zabijać / restartować aplikacji na Androidzie. W moim przypadku chcę przywrócić ustawienia fabryczne mojej aplikacji w konkretnym przypadku, w którym serwer wysyła określone informacje do klienta.
Użytkownik może być zalogowany tylko na serwerze z JEDNĄ instancją aplikacji (tzn. Wiele urządzeń nie jest dozwolonych). Jeśli inna instancja otrzyma tę blokadę „zalogowanego”, wówczas wszystkie inne instancje tego użytkownika muszą usunąć swoje dane (przywrócone do ustawień fabrycznych), aby zachować spójność.
Możliwe jest przymusowe uzyskanie blokady, ponieważ użytkownik może usunąć aplikację i zainstalować ją ponownie, co spowoduje inny identyfikator instancji, a użytkownik nie będzie już w stanie zwolnić blokady. Dlatego można siłą dostać blokadę.
Z powodu tej możliwości siły musimy zawsze sprawdzić w konkretnym przypadku, czy ma zamek. Odbywa się to (prawie) przy każdym żądaniu do serwera. Serwer może wysłać „błędny identyfikator blokady”. Jeśli zostanie wykryty, aplikacja kliencka musi usunąć wszystko.
To był przypadek użycia.
Mam Activity
A, który rozpoczyna logowanie Activity
L lub główny Activity
B aplikacji, w zależności od wartości sharedPrefs. Po uruchomieniu L lub B zamyka się tak, że działa tylko L lub B. Więc w przypadku, gdy użytkownik jest zalogowany, już działa B.
B uruchamia C. C wzywa startService
na IntentService
D. To powoduje następujący stos:
(A)> B> C> D
Z metody onHandleIntent D zdarzenie jest wysyłane do ResultReceiver R.
R obsługuje teraz to zdarzenie, udostępniając użytkownikowi okno dialogowe, w którym może zresetować aplikację do ustawień fabrycznych (usunąć bazę danych, sharedPrefs itp.)
Po przywróceniu ustawień fabrycznych chcę ponownie uruchomić aplikację (aby zamknąć wszystkie działania) i uruchomić ponownie tylko A, który następnie uruchamia logowanie Activity
L i kończy się:
(A)> L
Metoda onClick okna dialogowego wygląda następująco:
@Override
public void onClick(DialogInterface dialog, int which) {
// Will call onCancelListener
MyApplication.factoryReset(); // (Deletes the database, clears sharedPrefs, etc.)
Intent i = new Intent(MyApp.getContext(), A.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MyApp.getContext().startActivity(i);
}
I to jest MyApp
klasa:
public class MyApp extends Application {
private static Context context;
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
public static Context getContext() {
return context;
}
public static void factoryReset() {
// ...
}
}
Problem polega na tym, że jeśli korzystam FLAG_ACTIVITY_NEW_TASK
z działań B i C, nadal działają. Po naciśnięciu przycisku Wstecz przy logowaniu Activity
widzę C, ale chcę wrócić do ekranu głównego.
Jeśli nie ustawię FLAG_ACTIVITY_NEW_TASK
, otrzymuję błąd:
07-07 12:27:12.272: ERROR/AndroidRuntime(9512): android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
Nie mogę użyć działań ' Context
, ponieważ ServiceIntent
D może być również wywołane z zadania w tle, które jest uruchamiane przez AlarmManager
.
Jak więc rozwiązać ten problem, zmieniając stos aktywności na (A)> L?
źródło
Możesz po prostu zadzwonić:
Który jest używany w bibliotece ProcessPhoenix
Jako alternatywa:
Oto nieco ulepszona wersja odpowiedzi @Oleg Koshkin.
Jeśli naprawdę chcesz zrestartować swoją aktywność, w tym zabicie bieżącego procesu, spróbuj wykonać kod. Umieść go w HelperClass lub tam, gdzie jest to potrzebne.
Spowoduje to również ponowne zainicjowanie klas jni i wszystkich instancji statycznych.
źródło
AlarmManager
i źle się zachowują, jeśli korzystają z tego rozwiązania. Czy jest jakieś lepsze podejście?Jake Wharton niedawno opublikował swoją bibliotekę ProcessPhoenix , która robi to w niezawodny sposób. Zasadniczo wystarczy zadzwonić:
Biblioteka automatycznie zakończy działanie wywołujące, zabije proces aplikacji i ponownie uruchomi domyślną aktywność aplikacji.
źródło
<category android:name="android.intent.category.DEFAULT" />
do domyślnej aktywności <intent-filter> w manifeście aplikacji.Zmodyfikowałem nieco odpowiedź Ilya_Gazman, aby korzystać z nowych interfejsów API (IntentCompat jest przestarzały od API 26). Runtime.getRuntime (). Exit (0) wydaje się być lepsza niż System.exit (0).
źródło
System.exit(n)
jest faktycznie równoważne z połączeniem:Runtime.getRuntime().exit(n)
”. WewnętrznieSystem.exit()
po prostu się odwraca i dzwoniRuntime.getRuntime().exit()
. Nie ma nic „lepszego” w jednym lub drugim (chyba że ktoś jest zaniepokojony tym, jak dużo pisze się na maszynie lub jedną dodatkową warstwę wywołań metod).Runtime.getRuntime().exit(0)
(lubSystem.exit(0)
) prawdopodobnie będzie działać. Niektóre z moich „niezbyt dobrych” komentarzy dotyczą odpowiedzi (takich jak Ilya Gazman , które zostały zredagowane w celu włączenia takiego wezwania.IntentCompat.makeRestartActivityTask
Nowym sposobem na to jest użycie IntentCompat.makeRestartActivityTask
źródło
Application
obiektu. Dlatego wszelkiestatic
dane, dane zainicjowane podczas tworzeniaApplication
klas lub klas jni pozostają w obecnym stanie i nie są ponownie inicjowane.IntentCompat.makeRestartActivityTask
jest teraz przestarzałe . Jeśli ty przejrzysz kod źródłowy , jest to tak proste, jak dodanie flagIntent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
.Jest naprawdę fajna sztuczka. Mój problem polegał na tym, że niektóre naprawdę stare biblioteki jni C ++ wyciekły z zasobów. W pewnym momencie przestał działać. Użytkownik próbował zamknąć aplikację i uruchomić ją ponownie - bez rezultatu, ponieważ zakończenie działania nie jest tym samym, co zakończenie (lub zabicie) procesu. (Nawiasem mówiąc, użytkownik może przejść do listy uruchomionych aplikacji i zatrzymać ją stamtąd - to zadziałałoby, ale użytkownicy po prostu nie wiedzą, jak zakończyć aplikacje.)
Jeśli chcesz obserwować efekt tej funkcji, dodaj
static
zmienną do swojej aktywności i zwiększaj ją, powiedzmy, naciśnięcie przycisku. Jeśli zakończysz działanie aplikacji, a następnie uruchom ją ponownie, ta zmienna statyczna zachowa swoją wartość. (Jeśli aplikacja naprawdę została zakończona, zmienna otrzyma wartość początkową).(I muszę skomentować, dlaczego zamiast tego nie chciałem naprawiać błędu. Biblioteka została napisana kilkadziesiąt lat temu i przeciekała zasoby od tego czasu. Zarząd uważa, że zawsze działała . Koszt dostarczenia poprawki zamiast obejścia ... Myślę, że masz pomysł.)
Jak mogę zresetować bibliotekę współdzieloną jni (czyli dynamiczną, .so) do stanu początkowego? Zdecydowałem się zrestartować aplikację jako nowy proces.
Sztuka polega na tym, że System.exit () zamyka bieżącą aktywność, a Android odtwarza aplikację z jedną aktywnością mniejszą.
Więc kod to:
Działanie wywołujące po prostu wykonuje kod
MagicAppRestart.doRestart(this);
, działanie wywołująceonPause()
jest wykonywane, a następnie proces jest odtwarzany ponownie. I nie zapomnij wspomnieć o tej aktywności w AndroidManifest.xmlZaletą tej metody jest brak opóźnień.
UPD: działało w Androidzie 2.x, ale w Androidzie 4 coś się zmieniło.
źródło
System.exit(0)
przezandroid.os.Process.killProcess(android.os.Process.myPid());
? 2) nieskończona pętla najprawdopodobniej oznacza, że nie usuwają one najwyższej aktywności po ponownym uruchomieniu aplikacji. Zasadniczo można dodać statyczną zmienną logiczną, ustawić ją na true przed wywołaniem działania restartu, a po restarcie będzie to fałsz. W ten sposób aktywność może dowiedzieć się, czy restart już się odbył (a jeśli tak się stało, wystarczy zakończyć () ). OTOH, twój raport oznacza, że sztuczka nie działa identycznie na wszystkich urządzeniach.Moje rozwiązanie nie uruchamia ponownie procesu / aplikacji. Pozwala jedynie aplikacji „zrestartować” aktywność domową (i odrzucić wszystkie inne czynności). Dla użytkowników wygląda to na restart, ale proces jest taki sam. Myślę, że w niektórych przypadkach ludzie chcą osiągnąć ten efekt, więc zostawiam to tutaj FYI.
źródło
Ok, zreorganizowałem moją aplikację i nie ukończę A automatycznie. Pozwoliłem, aby biegło to zawsze i kończę to
onActivityResult
wydarzenie. W ten sposób mogę użyć flagFLAG_ACTIVITY_CLEAR_TOP
+,FLAG_ACTIVITY_NEW_TASK
aby uzyskać to, czego chcę:i w
ResultReceiver
W każdym razie dzięki!
źródło
źródło
Jedyny kod, który nie uruchomił się „Twoja aplikacja została nieoczekiwanie zamknięta” to: Jest to również nieaktualny kod, który nie wymaga zewnętrznej biblioteki. Nie wymaga również timera.
źródło
Przekonałem się, że działa to na API 29 i późniejszych - w celu zabicia i ponownego uruchomienia aplikacji tak, jakby użytkownik uruchomił ją, gdy nie była uruchomiona.
Stało się tak, gdy aktywność programu uruchamiającego w aplikacji ma następujące cechy:
Widziałem komentarze stwierdzające, że potrzebna jest kategoria DOMYŚLNA, ale nie znalazłem tego. Potwierdziłem, że obiekt Application w mojej aplikacji został ponownie utworzony, więc uważam, że proces naprawdę został zabity i ponownie uruchomiony.
Jedynym celem, z którego korzystam, jest ponowne uruchomienie aplikacji po tym, jak użytkownik włączy lub wyłączy raportowanie awarii Firebase Crashlytics. Zgodnie z ich dokumentami aplikacja musi zostać ponownie uruchomiona (proces zabity i ponownie utworzony), aby zmiana zaczęła obowiązywać.
źródło
Najlepszym sposobem na pełne zrestartowanie aplikacji jest jej ponowne uruchomienie, a nie tylko przejście do aktywności za pomocą
FLAG_ACTIVITY_CLEAR_TOP
iFLAG_ACTIVITY_NEW_TASK
. Więc moim rozwiązaniem jest zrobienie tego z Twojej aplikacji lub nawet z innej aplikacji, jedynym warunkiem jest znajomość nazwy pakietu aplikacji (przykład: „ com.example.myProject ”)Przykład ponownego uruchomienia lub uruchomienia aplikacji A z appB :
Możesz sprawdzić, czy aplikacja działa:
Uwaga : wiem, że ta odpowiedź jest nieco nie na temat, ale może być naprawdę pomocna dla kogoś.
źródło
Moim najlepszym sposobem na ponowne uruchomienie aplikacji jest użycie
finishAffinity();
Ponieważ,
finishAffinity();
można jej używać tylko w wersjach JELLY BEAN, więc możemy jej używaćActivityCompat.finishAffinity(YourCurrentActivity.this);
przypadku niższych wersji.Następnie użyj,
Intent
aby uruchomić pierwszą aktywność, aby kod wyglądał następująco:Mam nadzieję, że to pomoże.
źródło
Spróbuj użyć
FLAG_ACTIVITY_CLEAR_TASK
źródło
Oto przykład ponownego uruchomienia aplikacji w ogólny sposób przy użyciu PackageManager:
źródło
Application
obiektu. Dlatego wszelkie dane statyczne, dane zainicjowane podczas tworzeniaApplication
klas lub klas jni pozostają w bieżącym stanie i nie są ponownie inicjowane.Spróbuj tego:
źródło
Bezpośrednio uruchom ekran początkowy za pomocą
FLAG_ACTIVITY_CLEAR_TASK
iFLAG_ACTIVITY_NEW_TASK
.źródło
Musiałem dodać moduł obsługi, aby opóźnić wyjście:
źródło
Posługiwać się:
Wierzę, że działa od poziomu API 16 (4.1).
źródło
Możesz użyć
startInstrumentation
metodyActivity
. Potrzebujesz implementacji pustejInstrumentation
i wskazanej w manifeście. Następnie możesz wywołać tę metodę w celu ponownego uruchomienia aplikacji. Lubię to:Nazwę klasy Instrumentation otrzymuję dynamicznie, ale możesz ją na stałe zakodować. Niektórzy lubią to:
Zadzwoń,
startInstrumentation
przeładuj aplikację. Przeczytaj opis tej metody. Ale może to nie być bezpieczne, jeśli działa jak aplikacja typu kill.źródło
Aplikacja, nad którą pracuję, musi umożliwiać użytkownikowi wybór fragmentów do wyświetlenia (fragmenty są dynamicznie zmieniane w czasie wykonywania). Najlepszym rozwiązaniem dla mnie było całkowite ponowne uruchomienie aplikacji.
Wypróbowałem więc wiele rozwiązań i żadne z nich nie działało dla mnie, ale to:
Mam nadzieję, że pomoże to komuś innemu!
źródło
Spróbuj tego:
źródło
Z biblioteką Process Phoenix . Działanie, które chcesz ponownie uruchomić, nosi nazwę „A”.
Smak Java
Smak Kotlina
źródło
Możesz ponownie uruchomić bieżącą aktywność w następujący sposób:
Fragment:
Czynność :
źródło