Fehler bei Dateioperationen beim Absturz / Beenden des Spiels durch Betriebssystem / Benutzer in Android

13

Mein Spiel speichert seinen Status nach einem festgelegten Intervall in einer Datei im internen Speicher des Spiels / der App. Wenn mein Spiel vom Benutzer oder Betriebssystem beendet oder abgestürzt wird, schreiben wir den aktuellen Spielstatus in diese Datei. Das Schreiben von Dateien nach dem Fixintervall funktioniert einwandfrei, aber wenn das Spiel vom Betriebssystem oder Benutzer abgestürzt ist, schlägt der Schreibvorgang fehl. Ein Betriebsfehler führt zu einem unvollständigen oder leeren Spielstatus, dh es werden überhaupt keine Daten in die Datei geschrieben. Ich habe mehrere Lösungen mit Android-Dienst ausprobiert. Falls der Dienst NICHT STICKY ist, wird der Dienst mit der App beendet. Wenn der Dienst dagegen STICKY ist, wird er neu gestartet, aber die ursprünglich mit dem Dienst verknüpfte Absicht ist null oder neu.

Die Frage ist, wie kann ich meine Daten (könnte ~ 2-3 MB groß sein) vollständig im internen Speicher ablegen, wenn mein Spiel / meine App vom Benutzer / Betriebssystem beendet wird?

Faisal Imran
quelle
Sie müssen mit Ihren SIGTERMs und Ihren SIGKILLs umgehen (naja, vielleicht nicht mit SIGKILLs, Android verwendet wahrscheinlich SIGTERM). (Sie haben keine Zeit, eine vollständige Antwort zu recherchieren / aufzuschreiben, aber das ist die Grundidee.)
John Hamilton
2
@JohnHamilton SIGKILL kann per Definition nicht gehandhabt werden.
Darkhogg
@ Darkhogg Nun, nicht in Unity, das ist sicher. (Ich habe den Linux-Kernel an einem Punkt für ein Projekt für diesen speziellen Zweck geändert und es ist sicherlich möglich, dass Programme mit SIGKILL umgehen)
John Hamilton

Antworten:

20

Ich würde vorschlagen, Ihren Speicherstatus doppelt gepuffert zu schreiben, wie es in der Vergangenheit bei Konsolentiteln üblich war (bei denen das Entfernen der Speicherkarte während des Schreibvorgangs und das Schreiben nur langsam vonstatten ging).

Speichern:

  • Wenn keine Dateien vorhanden sind, schreiben Sie den Status in Datei A
  • Wenn A existiert, schreibe an B
  • Wenn sowohl A als auch B vorhanden sind, suchen Sie, welches älter ist, löschen Sie es und schreiben Sie in dieses

Belastung:

  • Wenn sowohl A als auch B vorhanden sind, versuchen Sie, das neuere der beiden zu laden. Wenn dies fehlschlägt (weil die Datei unvollständig oder beschädigt ist), löschen Sie sie und laden Sie die andere
  • Wenn nur einer vorhanden ist, laden Sie ihn
  • Wenn beide fehlerhaft sind, informieren Sie den Benutzer darüber, dass der Speicherstatus nicht wiederherstellbar war (wie Sie es vermutlich bereits getan haben).

Dieser Ablauf stellt sicher, dass immer mindestens eine gültige Speicherung in Ihrem Speicher vorhanden ist, auch wenn die App während des Schreibvorgangs beendet wird. Der Spieler erhält keine der Änderungen im Spielstatus seit dem letzten Speichern, wenn diese Art von Fehler auftritt, muss jedoch nicht erneut gestartet werden.

Zusätzlich sollten Sie unmittelbar nach Schlüsselereignissen (wie In-App-Käufen) zusätzlich zu Ihrer Intervallspeicherung speichern, um das Fenster der Sicherheitsanfälligkeit zu minimieren, in dem ein Absturz / Kill dazu führen würde, dass dieses Schlüsselereignis verloren geht. Dies ist auch ein Schutz gegen Spiel-Exploits (z. B. das erzwungene Beenden der App nach dem Verlust eines Lebens, da Sie wissen, dass Sie den Spielstatus wiederherstellen, bevor Sie ein Leben verloren haben, und es erneut versuchen können). In diesem Fall müssen Sie die Intervallspeicherung natürlich mit einer Logik schützen, die besagt: "Wenn bereits ein Speichervorgang ausgeführt wird, versuchen Sie nicht, den Speichervorgang erneut zu starten."

MrCranky
quelle
Dank MrCranky ist dies eine Teillösung für mein Problem. Wie kann ich die Daten der letzten Sitzung, dh kurz vor dem Beenden der App / des Spiels, speichern? Zum Beispiel hat er eine In-App gekauft und die App getötet.
Faisal Imran
12
Sie brauchen nicht einmal zwei Dateien. In B schreiben, wenn und nur wenn das Schreiben erfolgreich war, A löschen und B in A umbenennen. Das java.nio.file.Files.move(Path source, Path target, CopyOption... options)Umbenennen und Überschreiben kann als atomare Operation erfolgen.
Polygnome
6
@Polygnome: Beachten Sie, dass bei Stromausfällen nicht immer die erwarteten Garantien bereitgestellt werden, es sei denn, Sie rufen sync()Datei B auf, bevor Sie sie über Datei A verschieben (auch dann gibt es keine vollständigen Garantien, diese sind jedoch sync()recht gut).
Dietrich Epp
1
@FaisalImran Daten sofort nach dem wichtigen Vorgang speichern, z. B. nach IAP. Wenn der Vorgang durch Ausschalten des Telefons unterbrochen würde, würde die Person sowieso niemals Geld erhalten. Aber nur für den Fall, möchten Sie vielleicht seine Kontodaten bei einem Kauf auf einer Plattform speichern, um etwas zu kaufen. Später können Sie dann Ihr Einkommen mit diesen Daten vergleichen und feststellen, ob diese Person wirklich etwas gekauft hat. Und dann öffnen Sie diese Funktion für ihn über den Onlinedienst, mit dem Sie alle Daten speichern. Wenn Sie lokale Speicher für IAP verwenden, ist dieses Problem noch schlimmer.
Candid Moon_Max_
1
Obwohl dies möglicherweise nicht alle Ihre Probleme löst, würde ich sagen, dass Ihr Problem nicht vollständig lösbar ist. Sie können nicht beide Optionen unterstützen: "Der Benutzer kann meine App jederzeit beenden" und "Es dürfen nie Daten verloren gehen", da nach dem Eintreten eines Ereignisses immer ein Fenster angezeigt wird, bevor es an dem Speicherort gespeichert wird, an dem der Benutzer es gespeichert hat kann die App töten und Daten verlieren.
MrCranky
3

Android beendet Apps nur im Hintergrund. Müssen Ihre Spieldaten wirklich aktualisiert werden, wenn sich die App im Hintergrund befindet, oder können Sie die Aktualisierung der Daten beenden, bis die App in den Vordergrund zurückkehrt? Sie können Aktualisierungen möglicherweise auch in einem Multiplayer-Spiel zurückstellen, wenn Sie einen Verlauf von Ereignissen oder sogar nur den aktuellen Status abrufen können, wenn die App wieder in den Vordergrund rückt. Dies sollten Sie aus Gründen der CPU-, Netzwerk- und Akkueffizienz ohnehin in Betracht ziehen.

Wenn der Benutzer Ihre App beendet, sollten ausgeführte Dienste angerufen werden onTaskRemoved. Ich weiß nicht, was passieren würde, wenn Sie versuchen würden, in dieser Methode viel zu verarbeiten. Ich gehe davon aus, dass Android kill -9Ihre App wird, wenn es nicht innerhalb einer bestimmten Zeit zurückkehrt .

Kevin Krumwiede
quelle
Die App muss nicht im Hintergrund aktualisiert werden, aber die App muss ihre Daten, z. B. die JSON-Datei, speichern, bevor sie in den Hintergrund wechselt oder zerstört wird.
Faisal Imran
Sie haben Recht, die onTaskRemoved-Methode ruft auf, aber die zum Speichern der Datei erforderliche Zeit ist länger, oder wir können sagen, dass die Rechenzeit groß ist. Wenn der Benutzer die App beendet, wird diese Methode beendet.
Faisal Imran