Errore nelle operazioni sui file in caso di arresto anomalo / interruzione del gioco da parte del sistema operativo / utente in Android


13

Il mio gioco salva il suo stato dopo un intervallo fisso su un file nella memoria interna di gioco / app. Quando il mio gioco viene ucciso o arrestato rispettivamente dall'utente o dal sistema operativo, scriviamo lo stato di gioco corrente su quel file. La scrittura del file dopo l'intervallo di correzione funziona correttamente ma quando il gioco viene arrestato in modo anomalo / ucciso dal sistema operativo o dall'utente, l'operazione di scrittura del file non riesce. Il fallimento dell'operazione provoca uno stato di gioco incompleto o uno stato di gioco vuoto, cioè nessun dato scritto sul file. Ho provato più soluzioni utilizzando il servizio Android, nel caso in cui il servizio sia NON STICKY il servizio viene ucciso con l'app. D'altra parte, se il servizio è STICKY, viene riavviato, ma l'intento inizialmente collegato al servizio è nullo o nuovo.

La domanda è che come posso salvare i miei dati (potrebbe essere ~ 2-3 MB) completamente archiviati nella memoria interna quando il mio gioco / app viene ucciso dall'utente / sistema operativo?


Devi gestire i tuoi SIGTERMs e i tuoi SIGKILLs (beh forse non SIGKILLs, Android probabilmente sta usando SIGTERM). (Non ho tempo di cercare / scrivere una risposta completa, ma questa è l'idea di base.)
John Hamilton,

2
@JohnHamilton SIGKILL non può essere gestito, per definizione.
Darkhogg,

@Darkhogg Beh, non in Unity, questo è certo. (Ho cambiato il kernel Linux a un certo punto per un progetto per questo scopo specifico ed è certamente possibile lasciare che i programmi gestiscano SIGKILL)
John Hamilton,

Risposte:


20

Suggerirei di scrivere il tuo stato di salvataggio in un doppio buffer, come era comune per i titoli della console in passato (dove dovevi affrontare la scheda di memoria rimossa a metà scrittura e le scritture erano lente).

Salva:

  • Se non esistono file, scrivere lo stato nel file A
  • Se esiste A, scrivi a B
  • Se esistono A e B, trova quello più vecchio, eliminalo e quindi scrivi su quello

Caricare:

  • Se esistono sia A che B, prova a caricare il più recente dei due. Se fallisce (perché il file è incompleto o corrotto), cancellalo e carica l'altro
  • Se ne esiste solo uno, caricalo
  • Se entrambi sono danneggiati, informa l'utente che il loro stato di salvataggio era irrecuperabile (come presumibilmente devi già)

Questo flusso funzionerà per garantire che sia sempre presente almeno un salvataggio valido nella memoria, anche se l'app viene interrotta durante la scrittura. Il giocatore non riceverà nessuna delle modifiche allo stato del gioco dal salvataggio precedente se si verifica quel tipo di errore, ma non dovrà ricominciare.

Inoltre, dovresti salvare immediatamente dopo gli eventi chiave (come gli acquisti in-app) oltre al salvataggio degli intervalli, per ridurre al minimo la finestra di vulnerabilità in cui un arresto anomalo causerebbe la perdita di quell'evento chiave. Questa è anche protezione contro gli exploit di gioco (ad es. Uccidere forzatamente l'app dopo aver perso una vita, perché sai che tornerai a uno stato di gioco da prima di aver perso una vita e ci riprovi). Naturalmente, se lo fai, devi proteggere il tuo intervallo di salvataggio con la logica che dice "se un salvataggio è già in corso, non provare a ricominciare a salvare".


Grazie MrCranky questa è una soluzione parziale al mio problema, come posso salvare i dati dell'ultima sessione, cioè prima di uccidere app / gioco? Ad esempio, ha effettuato un acquisto in-app e ha ucciso un'app.
Faisal Imran,

12
Non hai nemmeno bisogno di due file. Scrivi a B, se e solo se la scrittura ha avuto esito positivo, elimina A e rinomina B in A. java.nio.file.Files.move(Path source, Path target, CopyOption... options)può fare rinominare e sovrascrivere come un'operazione atomica.
Polygnome,

6
@Polygnome: si noti che in caso di interruzioni di corrente non fornirà sempre le garanzie che ci si aspetta, a meno che non si chiami sync()sul file B prima di spostarlo sul file A (anche se non ci sono garanzie complete, ma sync()è abbastanza buono).
Dietrich Epp,

1
@FaisalImran Salva i dati subito dopo l'importante operazione, ad esempio dopo IAP. Se l'operazione fosse interrotta dallo spegnimento del telefono, suppongo che non avrebbe mai preso soldi dalla persona comunque. Ma nel caso, potresti voler salvare i dati del suo account in un acquisto su una piattaforma come tentativo di acquistare qualcosa. Quindi in seguito puoi confrontare i tuoi guadagni con quei dati e vedere se questa persona ha davvero acquistato qualcosa. E quindi apri questa funzione per lui tramite il servizio online che stai utilizzando per salvare tutti i dati. Se stai usando i salvataggi locali per IAP, è anche peggio di questo problema.
Candid Moon _Max_

1
Infine, anche se questo potrebbe non risolvere tutti i tuoi problemi, direi che il tuo problema non è completamente risolvibile. Non puoi supportare sia "l'utente può uccidere la mia app in qualsiasi momento" sia "nessun dato deve essere perso mai" perché ci sarà sempre una finestra dopo che un evento ha avuto luogo ma prima che sia stato persistito nella memoria in cui l'utente può uccidere l'app e perdere dati.
MrCranky,

3

Android uccide le app solo quando sono in background. I tuoi dati di gioco devono davvero essere aggiornati quando l'app è in background o puoi interrompere l'aggiornamento dei dati fino a quando l'app non torna in primo piano? Potresti essere in grado di rinviare gli aggiornamenti anche in una partita multiplayer, se riesci a raccogliere una cronologia degli eventi o anche solo lo stato corrente quando l'app torna in primo piano. Questo è qualcosa che dovresti probabilmente fare comunque per il bene della CPU, della rete e dell'efficienza della batteria.

Se l'utente uccide la tua app, i servizi in esecuzione dovrebbero ricevere una chiamata a onTaskRemoved. Non so cosa succederebbe se provassi a fare molta elaborazione con questo metodo. Mi aspetto che se non ritorna entro un certo tempo, Android sarà la kill -9tua app.


l'app non deve essere aggiornata in background, ma l'app deve salvare i suoi dati, ad esempio il file json prima di passare in background o distruggere.
Faisal Imran,

hai ragione il metodo CallRemoved chiamerà, ma il tempo necessario per salvare il file è maggiore o possiamo dire che il tempo di calcolo è grande. Quando l'utente uccide l'app, questo metodo verrà interrotto.
Faisal Imran,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.