In primo luogo, so che non si dovrebbe davvero uccidere / riavviare un'applicazione su Android. Nel mio caso d'uso desidero ripristinare le impostazioni di fabbrica della mia applicazione in un caso specifico in cui un server invia informazioni specifiche al client.
L'utente può accedere al server solo con UNA istanza dell'applicazione (ovvero non sono consentiti più dispositivi). Se un'altra istanza ottiene quel blocco "connesso", tutte le altre istanze di quell'utente devono eliminare i propri dati (ripristino delle impostazioni di fabbrica), per mantenere la coerenza.
È possibile ottenere forzatamente il blocco, poiché l'utente potrebbe eliminare l'app e reinstallarla, il che comporterebbe un diverso ID istanza e l'utente non sarebbe più in grado di liberare il blocco. Pertanto è possibile ottenere forzatamente il blocco.
A causa di quella possibilità-forza, dobbiamo sempre verificare in un'istanza concreta che abbia il lucchetto. Questo viene fatto su (quasi) ogni richiesta al server. Il server potrebbe inviare un "ID blocco errato". Se viene rilevato, l'applicazione client deve eliminare tutto.
Quello era il caso d'uso.
Ho una Activity
A che avvia la Activity
L di accesso o la Activity
B principale dell'app in base a un valore sharedPrefs. Dopo aver avviato L o B, si chiude in modo che solo L o B siano in esecuzione. Quindi nel caso in cui l'utente abbia già effettuato l'accesso B ora è in esecuzione.
B inizia C. C chiama startService
per IntentService
D. Ciò si traduce in questo stack:
(A)> B> C> D
Dal metodo onHandleIntent di D un evento viene inviato a un ResultReceiver R.
R ora gestisce quell'evento fornendo all'utente una finestra di dialogo in cui può scegliere di ripristinare le impostazioni di fabbrica dell'applicazione (eliminare il database, sharedPrefs, ecc.)
Dopo il ripristino delle impostazioni di fabbrica, voglio riavviare l'applicazione (per chiudere tutte le attività) e riavviare solo A, che avvia quindi il login Activity
L e termina:
(A)> L
Il metodo onClick della finestra di dialogo è simile al seguente:
@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);
}
E questa è la MyApp
classe:
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() {
// ...
}
}
Il problema è che se uso FLAG_ACTIVITY_NEW_TASK
le attività B e C sono ancora in esecuzione. Se premo il pulsante Indietro sull'accesso Activity
vedo C, ma voglio tornare alla schermata principale.
Se non imposto FLAG_ACTIVITY_NEW_TASK
il messaggio di errore, ottengo:
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?
Non posso usare le Attività ' Context
, perché la ServiceIntent
D potrebbe anche essere chiamata da un'attività in background che viene avviata da AlarmManager
.
Quindi, come potrei risolverlo nello stack di attività diventando (A)> L?