Wie verhindere ich die mehrfach Initialisierung von onCreate ?

Das macht kein Sinn. Der ActivityManager ist dafür uninteressant. Es kann nur eine Klasse dies verwalten, die nebenläufig ist. Die richtige Klasse ist der ActivityThread. Dort wird alles geregelt.

Denn Zusammenhang sind man schon, wenn man sich die Stacktrace von Exception anschaut.
Code:
E/AndroidRuntime(760):  at android.app.ActivityThread.main(ActivityThread.java:5039)
E/AndroidRuntime(760):  at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(760):  at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime(760):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
E/AndroidRuntime(760):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)

Und in der Doku steht explizit:
To save persistent data, such as user preferences or data for a database, you should take appropriate opportunities when your activity is in the foreground. If no such opportunity arises, you should save such data during the onStop() method.

es steht dort auch:
Because the onCreate() method is called whether the system is creating a new instance of your activity or recreating a previous one, you must check whether the state Bundle is null before you attempt to read it. If it is null, then the system is creating a new instance of the activity, instead of restoring a previous one that was destroyed.

The Activity Lifecycle | Android Developers

Wenn in der App die Methode finish() aufrufen wird, wird die Methode onDestroy() aufgerufen, und das Bundle ist weg. Es ist sogar weniger persistent als mein Vorschlag. ;)
 
Im Guide zu Activity The Activity Lifecycle | Android Developers wird explizit der Fall, dass das System den Prozess aufgrund Speichermangels killt im Abschnitt Saving and restoring activity state erwähnt, was für mich bedeutet, dass speichern und wiederherstellen auch in diesem Fall möglich ist.

Weiterhin in der Activity Doku zu onSaveInstanceState():
This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state. For example, if activity B is launched in front of activity A, and at some point activity A is killed to reclaim resources, activity A will have a chance to save the current state of its user interface via this method so that when the user returns to activity A, the state of the user interface can be restored via onCreate(Bundle) or onRestoreInstanceState(Bundle).

Auch hier so beschrieben: Android process death — and the (big) implications for your app – Inloop

markus.tullius schrieb:
Und in der Doku steht explizit:
To save persistent data, such as user preferences or data for a database, you should take appropriate opportunities when your activity is in the foreground. If no such opportunity arises, you should save such data during the onStop() method.

Irrelevant, da es immer noch um das Bundle geht, welches nur für temporäre Daten genutzt werden sollte. Ich will nicht darauf hinaus, dass das Bundle persistent ist, nur weil es einen Prozess-Kill übersteht.

markus.tullius schrieb:
Wenn in der App die Methode finish() aufrufen wird, wird die Methode onDestroy() aufgerufen, und das Bundle ist weg. Es ist sogar weniger persistent als mein Vorschlag. ;)

Ich würde sagen, dass finish() ein gewolltes gewolltes Beenden der Activity indiziert. Bei gewolltem Beenden, beispielsweise bei Nutzung des Zurück-Buttons wird der instanceState nicht gespeichert. Das Bundle überlebt wie oben beschrieben auch onDestroy()
 
a) Jede Activity hat einen eigenen ActivityThread. Der ActivityThread ist kein Prozess!!! Und hat auch nicht damit zu tun, außer das der Prozess die ActivityThreads hält. Es kann ein ActivityThread beendet werden, ohne das der Prozess, in dem die App läuft, beendet wird.

b) Die Referenz für das betreffende Bundle hält der ActivityThread. Es gibt keine Schreibzugriffe auf den persistenten Speicher! An die Activity wird das Bundle mit Callback übergeben. Sobald der Thread beendet wird, existiert einfach keine Referenz auf das Bundle. Und der GC räumt es weg.

Zusätzlich hat das System die Möglichkeit alte ActivityThread zu beenden. Das heißt die App muß nicht beendet werden, das ist ein eindeutiger Gewinn an Performance. Der Nachteil ist, das alle Daten im ActivityThread weg sind.

c) Es gibt nur drei Möglichkeiten, zwischen den einzelnen ActitvityThread Daten auzutauchen:
Application (Singleton).
Intents
und persistentes Speichern.

Und nicht von den drei Sachen trifft zu. Du darfst gerne nachschauen. Der Code ist Open Source.

Oder ganz einfach, wir unterhalten uns über die Lebenszyklus einer Activity und ihres Thread. Es ist nicht identisch mit den Lebenszyklus der App. Du verwechselst hier gerade Prozesse und Thread. ;)
 
Ich habe den Sourcecode mal durchgesehen und konnte tatsächlich keine Verbindung finden, die das Bundle vom Thread an den Manager übergibt.
Allerdings habe ich es getestet und es funktionierttrotzdem, dass der savedInstanceState wieder an die entsprechende Activity übergeben wird, nachdem der Application-Prozess vom System aufgrund von Speichermangel beendet wurde.
Dazu habe ich einen eigenen String in onSaveInstanceState gespeichert und in onCreate geprüft, ob dieser vorhanden ist. Nachdem ich die entsprechende Activity gestartet hatte, habe ich die App über den Home-Button in den Hintergrund verschoben und durch die entsprechenden Einstellungen bei den Entwickleroptionen erzwungen, dass nur nur ein Hintergrundprozess erlaubt ist. Wenn ich nun eine weitere App starte und diese ebenfalls in den Hintergrund verschiebe wird der Prozess meiner App gekillt.
Wenn ich meine App dann wieder über den App-Drawer aufrufe erscheint die entsprechende Activity und zeigt mir im Log, dass der String im Bundle vorhanden ist.

Scheinbar sichert das System das Bundle also auch wenn der Prozess gekillt wird. Hast du eine Idee wie?
 
Du hast gar nichts gemacht. Du hast den Prozess nicht gekillt. Der Prozess ist weg, wenn nichts im Hintergrund und im Vordergrund läuft. Du hast dich genau in den Parameter bewegt, die ich beschrieben habe. Solange etwas im Hintergrund und oder im Vordergrund läuft existiert auch der Prozess. Rufe mal System.exit(0) auf. und teste dann deine Vermutung. Wenn es funktioniert schaue ich noch mal im Code nach. Und sonst empfehle ich Dir das Studium der Doku.
 
markus.tullius schrieb:
Der Prozess ist weg, wenn nichts im Hintergrund und im Vordergrund läuft.
Was ich genau dadurch erzwinge, dass nur einer, oder eben auch gar kein Hintergrundprozess erlaubt ist. Außerdem habe ich den Prozess auch über AndroidStudio und DDMS beendet. Gleiches Ergebnis.
Stop Process im DDMS wirkt genauso wie System.exit(0); so wie ich das sehe.
 
Aber das Vorgehen, welches du vorher geschildert hast, killt nicht den Prozess der App, und darauf habe ich mich bezogen. Deine "Prozesse" sind Thread, die in einer Java VM laufen. Dies VM hat eine PID, nicht die einzelnen Thread, welche du oben beendet hast.

Was hast du genau gemacht? So wie ich das sehe, hast du in den Entwickler-Optionen einen aktiven Dienst gestoppt (Das ist der einzige Menüpunkt, den ich gefunden habe).
Dienst -> engl. Service. Das heißt du hast einen Service beendet. Leider pausieren die Activies weiterhin, spricht die ActivityThreads existieren weiter.
Wenn du die App wieder in den Vordergrund holst, macht sie genau das was sie soll. Sie zeigt die pausierten Activities an.
Sprich das ganze bewegt sich in den oben skizzierten Parameter.

Und im DDMS kannst du keine einzelnen Threads beenden.
 
Entwickleroptionen -> Punkt Apps -> Hintergrundprozesslimit: Keine Hintergrundprozesse oder Höchstens 1 Prozess.
Über DDMS: in AndroidStudio: Tools -> Android -> Android Device Monitor. Siehe Screenshot. Ich kann den Prozess der App im Ganzen auswählen und stoppen.
Diese beiden Vorgehen habe in bei mehreren SO-Fragen als empfohlene Varianten gefunden, um das Systemverhalten zu simulieren, wenn eine App im Hintergrund gekillt wird, um Speicher freizugeben.
qx3e6C5.png
 
DDMS:
Was du auf deinem Bild siehst, ist die VM! Die läuft in einem Prozess mit einer PID. Die PID ist 5758.
Mit ein bisschen Kenntnisse in Java und Linux sollte dir das klar sein.
Und das ganze hat nicht mit Thread zu tun. Die laufen alle in der VM. Du kanst ja mal die Thread anzeigen lasse, das geht mit dem DDMS. Dann sieht das schon ganz anders aus.

Device:
Jetzt hast du in den Settings eingestellt, dass nur ein Service im Hintergrund läuft. Es sollte dir eigentlich klar sein, das Activities nicht im Hintergrund laufen können. Denn das ist der Grund für den Lifecycle. Um zu verhindern, das Activities im Hintergrund weiter laufen. Das spart nämlich Strom, Datenvolumen usw.

Du mir bitte ein Gefallen, und beschäftige dich mehr mit den Grundlagen von Java und Android durch, bevor du den nächste Post schreibst.
[doublepost=1488102321,1488101404][/doublepost]---
Nachtrag:
Eine App hat meist mehr als 50.000 Objekte in mehren Threads im Heap. Ich weiß nicht, wie du von außen genau ein Thread zerstören willst. Bis jetzt dachte ich immer, die Kapselung, ein zentraler Bestandteil von jeder OOP -Sprache, verhindert dies. Ich würde mal vermuten, wenn du das machst, was du behauptest, hast du ein Weg gefunden, zentrale Sicherheitskonzepte von Java und Android auszuhebeln.
 
Ich will doch nur das Verhalten simulieren, dass die App bei Speichermangel beendet wird. Ich habe doch gar nicht vor nur einen Thread zu beenden. Das habe ich auch schonmal geschrieben.

Dazu habe ich zum einen die Einstellung des Hintergrundprozesslimits geutzt. Diese Einstellung sorgt dafür, dass die Prozesse von Apps im Hintergrund, also wenn kein Service läuft und keine Activity sichtbar ist, nicht erst bei Speichermangel, sondern schon früher, nämlich beim festgelegten Schwellenwert beendet werden.
Ich will NICHT einen einzelnen ActivityThread beenden, sondern den gesamten Prozess, ergo die VM.

Und genau bei diesem Verhalten sollte die Bundle der jeweiligen Activites gespeichert werden, und beim anschließenden Aufrufen der App aus dem Letzte Apps Menü wieder übergeben werden.
Exakt dieses Verhalten habe ich nachgestellt und es hat funktioniert.
 
So geht das nicht. Insbesondere nicht, wenn Du ein Smartphone benutzt, welches 6 GB Arbeitsspeicher hat. Da kann die VM problemlos auf 50 MB anwachsen.

Vielleicht wenn du ein Emulator benutzt, und den Arbeitsspeicher auf unter 1G beschränkst.

Das Verhalten kann man nicht wirklich simulieren, weil der ganze Foobar von mehren Faktoren abhängt, worauf du kein Einfluss hast. Die Speicherverwaltung wird vom System verwaltet. Und dein Activity war gerade aktiv. Dh. dein System räumt erst mal alles andere aus dem Speicher, bevor er sich an eine Activity in einem aktiven Prozess vergreift. Dann hängt es noch an deinem GC, und auf den hast du auch keine Einfluss.

Ich hatte mal auf der Arbeit ein Tablet, dass nur noch 100Mb Arbeitsspeicher frei hatte. Da hat das System völlig zufällig Prozesse beendet. War recht lustig. ;)
Der Prozess der App wurde fast nie beendet, es funktionierte trotzdem nicht so, wie man es erwartete.
 
Dass das schwierig zu simulieren ist ist mir klar. Mit der Einstellung Hintergrundprozesse sollte ich aber effektiv den freien Arbeitsspeicher irrelevant werden lassen, da dann statt Speichermangel das Prozesslimit als Trigger aktiv wird. Natürlich nur solange genug vorhanden ist.
Was meinst du wann die Activity aktiv war? Bei dem DDMS? Ich habe jeweils vor dem händischen Beenden des Prozesses die App über den Home-Button in den Hintergrund verschoben. Schließen per Zurück würde ja die Activity beenden, ohne onSaveInstanceState aufzurufen.

Wenn die Annahme, dass die Bundles außerhalb der Application vorgehalten werden stimmt, sollte der GC die ja auch nicht entsorgen. Auf dieser Annahme basiert ja meine Aussage, dass die Bundles ein Beenden des Prozesses wegen Speichermangels überleben. Ich habe wie weiter oben erwähnt nichts derartiges im Code gefunden. Trotzdem funktioniert es in meiner Simulation. Ob die Simulation 100% akkurat ist ist fragwürdig. Anhand meiner Ergebnisse und anhand von Berichten anderer auf u.a. SO würde ich persönlich aber davon ausgehen, dass es möglich ist den savedInstanceState trotz Prozesskill wiederherzustellen.

Das Erlebnis mit zu kleinem Arbeitsspeicher hatte ich auch auf meinem Galaxy Nexus. Wirklich unangenehm in der täglichen Nutzung.
 
Noch einmal ganz einfach:
1) Prozesse. Hier muss du unterscheiden. Es gibt in der Regel nur ein Prozess pro App. Anders ausgedrückt, in dem Fall ist der Prozess das laufende Programm.
Wenn du mehrere Prozesse haben willst, muss du das extra im Manifest deklarieren. Und dann hast du ganz andere Probleme.
Jeder Prozess hat genau ein MainThread, der alle anderen Threads hält.

2) Hintergrund und Vordergrund:
Die Unterscheidung ist recht einfach. Alles was mit der sichtbaren GUI zu tun hat, ist Vordergrund, der Rest ist Hintergrund.
Das heißt, die Activity haben eine GUI, also laufen sie im Vordergrund, Service haben keine GUI, also laufende sie im Hintergrund.
Hier wird gerne der Begriff Prozesse benutzt, aber es sind fast immer Thread (außer es ist im Manifest anders definiert).


3) Activity
Damit es kein seltsame Artefakte auf dem Display auftaucht, findet der Aufbau der GUI bei der Activity im Hintergrund statt. Dafür gibt es die Methoden wie onCreate. Gleiches gilt für den Fall, dass eine Activity nicht mehr sichtbar sein soll, auch dafür gibt es Methoden.
Wenn die Activity im Hintergrund ist, ist sie bis auf extrem kurze Phasen inaktiv. Sie läuft nicht. Sie ist ein totes Konstrukt, welches jederzeit aufgeweckt werden kann. Sie braucht deshalb auch nicht beendet werden.


4) Was hast du gemacht. Du sorgst dafür, das die Activity nicht mehr sichtbar ist. Sie ist noch kurz im Hintergrund tätig, und schläft dann. Als nächstes beendest alle laufende Dienste (Service). Da die Activity nicht aktiv ist, kannst du sie nicht beenden. Also existiert sie weiter.
Denn der entscheidende Faktor ist, dass du nicht die VM beendest. Die VM beendest nur, wenn du den Prozess komplett beendest!!

5)
Dazu habe ich einen eigenen String in onSaveInstanceState gespeichert und in onCreate geprüft, ob dieser vorhanden ist. Nachdem ich die entsprechende Activity gestartet hatte, habe ich die App über den Home-Button in den Hintergrund verschoben und durch die entsprechenden Einstellungen bei den Entwickleroptionen erzwungen, dass nur nur ein Hintergrundprozess erlaubt ist.
Falsch, du hast ein schlafenden ActivityThread, der nicht im Hintergrund aktiv ist. Und vielleicht ein Service.

Wenn ich nun eine weitere App starte und diese ebenfalls in den Hintergrund verschiebe wird der Prozess meiner App gekillt.
Nein, das ein Trugschluss. Wenn es so wäre, würde dein Smartphone einfach nur schwarz. Da sind eine ganze Menge Systemprozesse, die im Hintergrund weiterlaufen.
Ich habe in den Einstellungen die Option aktiviert, dass kein Hintergrundprozess möglich ist, und kann trotzdem über das Menu zwischen "offene" Apps wechseln. Das dürfte nicht gehen, wenn du recht hast. ;)
In dem Entwickler - Menu gibt es noch ein Einstellung - Inaktive Apps. Schau mal nach, ob deine App dort weiter angezeigt wird.
 
markus.tullius schrieb:
Nein, das ein Trugschluss. Wenn es so wäre, würde dein Smartphone einfach nur schwarz. Da sind eine ganze Menge Systemprozesse, die im Hintergrund weiterlaufen.
Ich habe in den Einstellungen die Option aktiviert, dass kein Hintergrundprozess möglich ist, und kann trotzdem über das Menu zwischen "offene" Apps wechseln. Das dürfte nicht gehen, wenn du recht hast. ;)
Dass sich die Einstellung des Hintergrundprozesslimits nicht auf Systemprozesse auswirkt ist klar. Aber alle vom User gestarteten Prozesse sollten davon betroffen sein. Natürlich werden sie dann aber auch nur beendet, sofern aktuell keine Activity aktiv ist, also onStop() aufgerufen wurde. Ausserdem darf vom Prozess kein service aktiv sein. Damit ist die App im Hintergrund, soll in diesem Fall heißen, dass sie komplett inaktiv ist.

Wenn ich jetzt eingestellt habe. dass ich keine Hintergrundprozesse, also solche wie gerade beschrieben, haben will, sollte das System die entsprechenden Prozesse, also wieder die VM beenden. Dabei wird aber onSaveInstanceState für alle Activites im Speicher aufgerufen, um sicherzugehen, dass keine Daten verloren gehen.
Deswegen kannst du auch die "offenen" apps wieder aufrufen und es scheint so, als wäre nichts passiert. Bei einigen Apps ist allerdings eine Verzögerung beim Starten zu sehen, die beim Aufrufen aus dem Speicher nicht auftreten würde. Chrome bspw muss dann erstmal die URL wieder eintragen um dann die aktuelle Website neu zu laden.

markus.tullius schrieb:
Denn der entscheidende Faktor ist, dass du nicht die VM beendest. Die VM beendest nur, wenn du den Prozess komplett beendest!!
Was ich ja nach deinen vorherigen Aussagen auch mit DDMS gemacht habe. Den Prozesse habe ich auf jeden Fall auf die eine oder andere Weise mal beendet. Und danach konnte ich das Bundle savedInstanceState auslesen und meinen String loggen.
 
OT : Nur mal so nebenbei :
Es macht Freude eurer Promotions-Zulassung zu folgen :) ^^
 
:lol: Gern geschehen.

@Vacutainer
Ich habe nur eine Frage:
Hast du im Mainifest ein Hintergrundprozess deklariert?
Etwas in der Form:
Code:
android:process=":foobar"
Wenn nicht, dann hast du keinen Hintergrundprozess!
Dann gilt folgendes:
By default, all components of the same application run in the same process and most applications should not change this. However, if you find that you need to control which process a certain component belongs to, you can do so in the manifest file.
Processes and Threads | Android Developers

Oder anders gesagt, wo kein Hintergrundprozess ist, da kann auch nichts beendet werden. ;) Du erklärst gerade ausführlich, dass du Nichts beendest. Und weil du Nichts beenden kannst, gilt deine Behauptung. Ich habe Probleme dabei.

"Chrome" ist nicht Open Source. Sprich keiner von uns beiden weiß, wie der Sourcecode aussiehst. Das heißt, ich kann deine Aussagen nicht überprüfen. Das heißt, du behauptest einfach mal etwas, und hoffst, dass ich es glaube (Fake).
 
Hallo,

die Aufrufe von onSaveInstanceState und onRestoreInstanceState lassen sich zumindest im Emulator durch einen simulierten Anruf nachstellen.
adb shell am start -a android.intent.action.CALL tel:1112223333
oder einfach durch Wechsel der Ansicht (Landscape/Portrait) - das gleiche übrigens am realen Gerät.
Das onSaveInstanceState vor Android 3.0 nicht funktioniert hätte, daran mag ich mich jetzt nicht erinnern - ich nutze das aber schon länger und
es funktioniert ganz gut. Muss es ja auch, denn alle Inhalte aus Eingabefeldern werden richtig restauriert wenn onSaveInstanceState/onRestoreInstanceState
nicht überschrieben werden und die Werte werden nicht restauriert wenn diese Methoden überschreiben - ohne Aufruf der Basis-Klassen-Methoden - werden.
 
Das ist aber nicht das Problem, ich glaube, da sind wir beiden Streithähne einig.
Bei deinen Fall bewegst du dich im Lifecycle der Activity.
Es geht, so glaube ich wenigsten, darum, was passiert, wenn die Activity und das Application - Objekt außerplanmäßig zerstört werden.
Ich behaupte, dass er mit seinem Weg nicht die Activity zerstört, weshalb die Daten auch wieder hergestellt werden können. Er glaubt das die Activity nicht mehr existiert, und deshalb die Daten aus persistent gespeicherten Daten wieder rekonstruiert werden.

Vielleicht kann uns hier jemand erhellen? :confused2:
 
Okay, es wird keine Arbeit im Hintergrund durchgeführt. Da bin ich dabei.
Vielleicht sollte ich statt "im Hintergrund" sagen, dass die App im Speicher behalten wird. Also alle Activities gestoppt, kein Service, kein Nichts. Das System hält nur alles im Speicher vor, damit die App schneller wieder aufgerufen werden kann. Ich behaupte mal, dass in der Einstellung Hintergrundprozesslimit davon die Rede ist.
Durch Ändern der Einstellung gebe ich dem System jetzt vor, wann solche "Hintergrundprozesse" bzw Apps im Speicher aus diesem entfernt werden. Normalerweise ja nur, wenn der Speicher knapp wird. Das wäre das Standardlimit.
 
Dann haben wir uns ja angenähert. :)
Noch ein Zusatz, es gibt die Möglichkeit, nicht nur Hintergrundprozesse zu beenden, sondern auch einzelne Komponenten eines Prozess.
 

Ähnliche Themen

M
Antworten
3
Aufrufe
146
moin
M
L
Antworten
15
Aufrufe
905
jogimuc
J
S
Antworten
17
Aufrufe
550
jogimuc
J
Zurück
Oben Unten