Erstens weiß ich, dass man eine Anwendung auf Android nicht wirklich beenden sollte. In meinem Anwendungsfall möchte ich meine Anwendung in einem bestimmten Fall auf die Werkseinstellungen zurücksetzen, in dem ein Server bestimmte Informationen an den Client sendet.
Der Benutzer kann nur mit EINER Instanz der Anwendung am Server angemeldet sein (dh mehrere Geräte sind nicht zulässig). Wenn eine andere Instanz diese "angemeldete" Sperre erhält, müssen alle anderen Instanzen dieses Benutzers ihre Daten löschen (auf die Werkseinstellungen zurücksetzen), um die Konsistenz aufrechtzuerhalten.
Es ist möglich, die Sperre zwangsweise zu erhalten, da der Benutzer die App möglicherweise löschen und neu installieren kann, was zu einer anderen Instanz-ID führen würde und der Benutzer die Sperre nicht mehr aufheben kann. Daher ist es möglich, das Schloss gewaltsam zu bekommen.
Aufgrund dieser Kraftmöglichkeit müssen wir immer in einer konkreten Instanz überprüfen, ob sie das Schloss hat. Dies geschieht bei (fast) jeder Anfrage an den Server. Der Server sendet möglicherweise eine "falsche Sperr-ID". Wenn dies erkannt wird, muss die Clientanwendung alles löschen.
Das war der Anwendungsfall.
Ich habe ein Activity
A, das das Login Activity
L oder das Haupt- Activity
B der App abhängig von einem sharedPrefs-Wert startet. Nach dem Start von L oder B schließt es sich so, dass nur L oder B läuft. Für den Fall, dass der Benutzer bereits angemeldet ist, wird B jetzt ausgeführt.
B startet C. C fordert startService
das IntentService
D. Das ergibt diesen Stapel:
(A)> B> C> D.
Von der onHandleIntent-Methode von D wird ein Ereignis an einen ResultReceiver R gesendet .
R behandelt dieses Ereignis jetzt, indem es dem Benutzer einen Dialog zur Verfügung stellt, in dem er die Anwendung auf die Werkseinstellungen zurücksetzen kann (Löschen der Datenbank, sharedPrefs usw.).
Nach dem Zurücksetzen auf die Werkseinstellungen möchte ich die Anwendung neu starten (um alle Aktivitäten zu schließen) und erst wieder A starten, das dann das Login Activity
L startet und sich selbst beendet:
(A)> L.
Die onClick-Methode des Dialogs sieht folgendermaßen aus:
@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);
}
Und das ist die MyApp
Klasse:
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() {
// ...
}
}
Das Problem ist, wenn ich FLAG_ACTIVITY_NEW_TASK
die Aktivitäten B und C verwende, laufen sie noch. Wenn ich beim Anmelden auf die Schaltfläche "Zurück" drücke, wird Activity
C angezeigt, ich möchte jedoch zum Startbildschirm zurückkehren.
Wenn ich das nicht einstelle, FLAG_ACTIVITY_NEW_TASK
erhalte ich den Fehler:
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?
Ich kann die Aktivitäten nicht verwenden Context
, da das ServiceIntent
D möglicherweise auch von einer Hintergrundaufgabe aufgerufen wird, die von der gestartet wird AlarmManager
.
Wie könnte ich das lösen, damit der Aktivitätsstapel (A)> L wird?
quelle
Sie können einfach anrufen:
Welches in der ProcessPhoenix- Bibliothek verwendet wird
Als Alternative:
Hier ist eine etwas verbesserte Version der Antwort von @Oleg Koshkin.
Wenn Sie Ihre Aktivität wirklich neu starten möchten, einschließlich eines Abbruchs des aktuellen Prozesses, versuchen Sie, den folgenden Code zu verwenden. Legen Sie es in eine HelperClass oder wo Sie es brauchen.
Dadurch werden auch jni-Klassen und alle statischen Instanzen neu initialisiert.
quelle
AlarmManager
und verhalten sich schlecht, wenn sie diese Lösung verwenden. Gibt es einen besseren Ansatz?Jake Wharton hat kürzlich seine ProcessPhoenix- Bibliothek veröffentlicht, die dies auf zuverlässige Weise tut. Sie müssen grundsätzlich nur anrufen:
Die Bibliothek beendet automatisch die aufrufende Aktivität, beendet den Anwendungsprozess und startet anschließend die Standardanwendungsaktivität neu.
quelle
<category android:name="android.intent.category.DEFAULT" />
zu Ihrer Standardaktivität <intent-filter> im App-Manifest hinzufügen .Ich habe die Antwort von Ilya_Gazman leicht geändert, um neue APIs zu verwenden (IntentCompat ist ab API 26 veraltet). Runtime.getRuntime (). Exit (0) scheint besser zu sein als System.exit (0).
quelle
System.exit(n)
entspricht effektiv dem Anruf:Runtime.getRuntime().exit(n)
". InternSystem.exit()
dreht sich nur um und ruft anRuntime.getRuntime().exit()
. Es gibt nichts "Besseres" an dem einen oder anderen (es sei denn, man ist besorgt darüber, wie viel man tippt oder über eine zusätzliche Ebene von Methodenaufrufen).Runtime.getRuntime().exit(0)
(oderSystem.exit(0)
) beinhalten, werden wahrscheinlich funktionieren. Einige meiner "nicht guten" Kommentare sind für Antworten (wie der von Ilya Gazman , die seitdem bearbeitet wurden, um einen solchen Aufruf aufzunehmen)IntentCompat.makeRestartActivityTask
Die neue Methode ist die Verwendung von IntentCompat.makeRestartActivityTask
quelle
Application
Objekt werden jedoch nicht neu gestartet . Daher bleiben allestatic
Daten, Daten, die während der Erstellung derApplication
oder jni-Klassen initialisiert wurden , in ihrem aktuellen Zustand und werden nicht neu initialisiert.IntentCompat.makeRestartActivityTask
ist jetzt veraltet . Wenn Sie den Quellcode überprüfen , müssen Sie lediglich die Flags hinzufügenIntent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
.Es gibt einen wirklich schönen Trick. Mein Problem war, dass in einer wirklich alten C ++ jni-Bibliothek Ressourcen verloren gingen. Irgendwann funktionierte es nicht mehr. Der Benutzer hat versucht, die App zu beenden und erneut zu starten - ohne Ergebnis, da das Beenden einer Aktivität nicht mit dem Beenden (oder Beenden) des Prozesses identisch ist. (Übrigens könnte der Benutzer zur Liste der ausgeführten Anwendungen gehen und sie von dort aus stoppen - dies würde funktionieren, aber die Benutzer wissen einfach nicht, wie sie Anwendungen beenden sollen.)
Wenn Sie den Effekt dieser Funktion beobachten möchten, fügen Sie a hinzu
static
Ihrer Aktivität Variable hinzu und erhöhen Sie sie beispielsweise durch Drücken einer Taste. Wenn Sie die Anwendungsaktivität beenden und die Anwendung dann erneut aufrufen, behält diese statische Variable ihren Wert. (Wenn die Anwendung wirklich beendet wurde, wird der Variablen der Anfangswert zugewiesen.)(Und ich muss kommentieren, warum ich den Fehler nicht stattdessen beheben wollte . Die Bibliothek wurde vor Jahrzehnten geschrieben und hat seitdem Ressourcen verloren. Das Management glaubt, dass es immer funktioniert hat . Die Kosten für die Bereitstellung eines Fixes anstelle einer Problemumgehung ... Ich denke, du kommst auf die Idee.)
Wie kann ich nun eine gemeinsam genutzte jni-Bibliothek (auch bekannt als dynamic, .so) auf den Ausgangszustand zurücksetzen? Ich habe mich entschieden, die Anwendung als neuen Prozess neu zu starten.
Der Trick besteht darin, dass System.exit () die aktuelle Aktivität schließt und Android die Anwendung mit einer Aktivität weniger neu erstellt.
Der Code lautet also:
Die aufrufende Aktivität führt nur den Code aus
MagicAppRestart.doRestart(this);
, die aufrufende AktivitätonPause()
wird ausgeführt und dann wird der Prozess neu erstellt. Und vergessen Sie nicht, diese Aktivität in AndroidManifest.xml zu erwähnenDer Vorteil dieser Methode ist, dass es keine Verzögerungen gibt.
UPD: Es hat in Android 2.x funktioniert, aber in Android 4 hat sich etwas geändert.
quelle
System.exit(0)
durchandroid.os.Process.killProcess(android.os.Process.myPid());
? 2) Eine Endlosschleife bedeutet höchstwahrscheinlich, dass sie beim Neustart einer App nicht die oberste Aktivität entfernen. Im Prinzip können Sie eine statische boolesche Variable hinzufügen, diese vor dem Aufrufen der Neustartaktivität auf true setzen und nach dem Neustart ist sie false. Auf diese Weise kann die Aktivität herausfinden, ob der Neustart bereits stattgefunden hat oder nicht (und wenn dies geschehen ist, beenden Sie einfach () ). OTOH, Ihr Bericht bedeutet, dass der Trick nicht auf allen Geräten identisch funktioniert.Meine Lösung startet den Prozess / die Anwendung nicht neu. Die App kann nur die Heimaktivität "neu starten" (und alle anderen Aktivitäten schließen). Für Benutzer sieht es nach einem Neustart aus, aber der Vorgang ist der gleiche. Ich denke, in einigen Fällen wollen die Leute diesen Effekt erzielen, also lasse ich ihn einfach hier, zu Ihrer Information.
quelle
Ok, ich habe meine App überarbeitet und werde A nicht automatisch beenden. Ich lasse das immer laufen und beende es durch die
onActivityResult
Veranstaltung. Auf diese Weise kann ich dieFLAG_ACTIVITY_CLEAR_TOP
+FLAG_ACTIVITY_NEW_TASK
Flags verwenden, um das zu bekommen, was ich will:und in der
ResultReceiver
Danke trotzdem!
quelle
quelle
Der einzige Code, der nicht "Ihre App wurde unerwartet geschlossen" ausgelöst hat, lautet wie folgt. Es ist auch nicht veralteter Code, für den keine externe Bibliothek erforderlich ist. Es ist auch kein Timer erforderlich.
quelle
Ich habe festgestellt, dass dies unter API 29 und höher funktioniert - zum Beenden und Neustarten der App, als hätte der Benutzer sie gestartet, als sie nicht ausgeführt wurde.
Dies geschah, wenn die Launcher-Aktivität in der App Folgendes aufweist:
Ich habe Kommentare gesehen, in denen behauptet wurde, dass eine Kategorie von DEFAULT erforderlich ist, aber ich habe nicht festgestellt, dass dies der Fall ist. Ich habe bestätigt, dass das Anwendungsobjekt in meiner App neu erstellt wurde, daher glaube ich, dass der Prozess tatsächlich beendet und neu gestartet wurde.
Der einzige Zweck, für den ich dies verwende, besteht darin, die App neu zu starten, nachdem der Benutzer die Absturzberichterstattung für Firebase Crashlytics aktiviert oder deaktiviert hat. Gemäß ihren Dokumenten muss die App neu gestartet (Prozess abgebrochen und neu erstellt) werden, damit diese Änderung wirksam wird.
quelle
Der beste Weg, eine App vollständig neu zu starten, besteht darin, sie neu zu starten und nicht nur mit
FLAG_ACTIVITY_CLEAR_TOP
und zu einer Aktivität zu springenFLAG_ACTIVITY_NEW_TASK
. Meine Lösung besteht also darin, dies von Ihrer App oder sogar von einer anderen App aus zu tun. Die einzige Bedingung besteht darin, den Namen des App-Pakets zu kennen (Beispiel: ' com.example.myProject ').Beispiel für den Neustart oder den Start von AppA über AppB :
Sie können überprüfen, ob die App ausgeführt wird:
Hinweis : Ich weiß, dass diese Antwort etwas unangebracht ist, aber für jemanden sehr hilfreich sein kann.
quelle
Mein bester Weg, um die Anwendung neu zu starten, ist die Verwendung von
finishAffinity();
Da,
finishAffinity();
kann nur in JELLY BEAN-Versionen verwendet werden, sodass wir verwenden könnenActivityCompat.finishAffinity(YourCurrentActivity.this);
für niedrigere Versionen verwenden können.Verwenden Sie dann
Intent
, um die erste Aktivität zu starten, damit der Code folgendermaßen aussieht:Ich hoffe es hilft.
quelle
Versuchen Sie es mit
FLAG_ACTIVITY_CLEAR_TASK
quelle
Hier ist ein Beispiel, um Ihre App mithilfe des PackageManager generisch neu zu starten:
quelle
Application
Objekt werden jedoch nicht neu gestartet . DaherApplication
bleiben statische Daten, Daten, die während der Erstellung der oder jni-Klassen initialisiert wurden , in ihrem aktuellen Zustand und werden nicht neu initialisiert.Versuche dies:
quelle
Starten Sie den Einstiegsbildschirm direkt mit
FLAG_ACTIVITY_CLEAR_TASK
undFLAG_ACTIVITY_NEW_TASK
.quelle
Ich musste einen Handler hinzufügen, um den Exit zu verzögern:
quelle
Verwenden:
Ich glaube, es funktioniert ab API-Level 16 (4.1).
quelle
Sie können die
startInstrumentation
Methode von verwendenActivity
. Sie müssen das Gerät leerInstrumentation
und im Manifest zeigen. Danach können Sie diese Methode aufrufen, um Ihre App neu zu starten. So was:Ich erhalte den Namen der Instrumentierungsklasse dynamisch, aber Sie können ihn fest codieren. Etwas wie das:
Rufen Sie
startInstrumentation
make make reload Ihrer App auf. Lesen Sie die Beschreibung dieser Methode. Aber es kann nicht sicher sein, wenn man sich wie eine Kill-App verhält.quelle
Die Anwendung, an der ich arbeite, muss dem Benutzer die Möglichkeit geben, auszuwählen, welche Fragmente angezeigt werden sollen (Fragmente werden zur Laufzeit dynamisch geändert). Die beste Lösung für mich war ein vollständiger Neustart die Anwendung .
Also habe ich viele Lösungen ausprobiert und keine davon hat für mich funktioniert, aber dies:
Ich hoffe, das hilft jemand anderem!
quelle
Versuche dies:
quelle
Mit der Process Phoenix-Bibliothek . Die Aktivität, die Sie neu starten möchten, heißt "A".
Java-Geschmack
Kotlin Geschmack
quelle
Sie können Ihre aktuelle Aktivität folgendermaßen neu starten:
Fragment :
Aktivität:
quelle