¿Cómo "reinicio" mediante programación una aplicación de Android?

Resuelto Stuck asked hace 55 años • 0 respuestas

En primer lugar, sé que uno no debería realmente cerrar/reiniciar una aplicación en Android. En mi caso de uso, quiero restablecer mi aplicación de fábrica en un caso específico en el que un servidor envía información específica al cliente.

El usuario sólo puede iniciar sesión en el servidor con UNA instancia de la aplicación (es decir, no se permiten varios dispositivos). Si otra instancia obtiene ese bloqueo de "iniciar sesión", todas las demás instancias de ese usuario deben eliminar sus datos (restablecimiento de fábrica) para mantener la coherencia.

Es posible obtener el bloqueo a la fuerza porque el usuario podría eliminar la aplicación y reinstalarla, lo que daría como resultado una identificación de instancia diferente y el usuario ya no podría liberar el bloqueo. Por lo tanto, es posible forzar el bloqueo.

Debido a esa posibilidad de fuerza, siempre debemos verificar en un caso concreto que tenga el bloqueo. Esto se hace en (casi) cada solicitud al servidor. El servidor podría enviar un "ID de bloqueo incorrecto". Si se detecta eso, la aplicación cliente debe eliminar todo.


Ese fue el caso de uso.

Tengo una ActivityA que inicia el inicio de sesión ActivityL o la B principal de la aplicación Activitydependiendo de un valor de SharedPrefs. Después de iniciar L o B, se cierra solo para que solo L o B esté funcionando. Entonces, en el caso de que el usuario haya iniciado sesión, B ya se está ejecutando.

B inicia C. C pide startServiceD. IntentServiceEso da como resultado esta pila:

(A) > B > C > D

Desde el método onHandleIntent de D, se envía un evento a un ResultReceiver R.

R ahora maneja ese evento proporcionando al usuario un cuadro de diálogo donde puede elegir restablecer la aplicación a los valores de fábrica (eliminar la base de datos, preferencias compartidas, etc.)

Después del restablecimiento de fábrica, quiero reiniciar la aplicación (para cerrar todas las actividades) y solo iniciar A nuevamente, lo que luego inicia el inicio de sesión ActivityL y finaliza solo:

(A) > L

El método onClick del diálogo se ve así:

@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);
}

Y esa es la MyAppclase:

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() {
        // ...
    }
}

El problema es que si uso las FLAG_ACTIVITY_NEW_TASKactividades B y C todavía se están ejecutando. Si presiono el botón Atrás al iniciar sesión, Activityveo C, pero quiero volver a la pantalla de inicio.

Si no configuro el FLAG_ACTIVITY_NEW_TASKme sale el error:

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?

No puedo usar las Actividades Contextporque ServiceIntenttambién se puede llamar a D desde una tarea en segundo plano iniciada por AlarmManager.

Entonces, ¿cómo podría resolver esto para que la pila de actividades se convierta en (A) > L?

Stuck avatar Jan 01 '70 08:01 Stuck
Aceptado

Puede utilizar PendingIntentpara configurar el inicio de su actividad de inicio en el futuro y luego cerrar su aplicación.

Intent mStartActivity = new Intent(context, StartActivity.class);
int mPendingIntentId = 123456;
PendingIntent mPendingIntent = PendingIntent.getActivity(context, mPendingIntentId,    mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
System.exit(0);
Oleg Koshkin avatar Jun 18 '2013 10:06 Oleg Koshkin

Modifiqué ligeramente la respuesta de Ilya_Gazman para usar nuevas API (IntentCompat está obsoleto a partir de API 26). Runtime.getRuntime().exit(0) parece ser mejor que System.exit(0).

 public static void triggerRebirth(Context context) {
    PackageManager packageManager = context.getPackageManager();
    Intent intent = packageManager.getLaunchIntentForPackage(context.getPackageName());
    ComponentName componentName = intent.getComponent();
    Intent mainIntent = Intent.makeRestartActivityTask(componentName);
    // Required for API 34 and later
    // Ref: https://developer.android.com/about/versions/14/behavior-changes-14#safer-intents
    mainIntent.setPackage(context.getPackageName());
    context.startActivity(mainIntent);
    Runtime.getRuntime().exit(0);
}
android_dev avatar Oct 20 '2017 11:10 android_dev

Puedes simplemente llamar:

public static void triggerRebirth(Context context, Intent nextIntent) {
    Intent intent = new Intent(context, YourClass.class);
    intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
    intent.putExtra(KEY_RESTART_INTENT, nextIntent);
    context.startActivity(intent);
    if (context instanceof Activity) {
      ((Activity) context).finish();
    }

    Runtime.getRuntime().exit(0);
}

Que se utiliza en la biblioteca ProcessPhoenix


Como alternativa:

Aquí hay una versión un poco mejorada de la respuesta de @Oleg Koshkin.

Si realmente desea reiniciar su actividad, incluida la finalización del proceso actual, intente seguir el código. Colóquelo en una HelperClass o donde lo necesite.

public static void doRestart(Context c) {
        try {
            //check if the context is given
            if (c != null) {
                //fetch the packagemanager so we can get the default launch activity 
                // (you can replace this intent with any other activity if you want
                PackageManager pm = c.getPackageManager();
                //check if we got the PackageManager
                if (pm != null) {
                    //create the intent with the default start activity for your application
                    Intent mStartActivity = pm.getLaunchIntentForPackage(
                            c.getPackageName()
                    );
                    if (mStartActivity != null) {
                        mStartActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        //create a pending intent so the application is restarted after System.exit(0) was called. 
                        // We use an AlarmManager to call this intent in 100ms
                        int mPendingIntentId = 223344;
                        PendingIntent mPendingIntent = PendingIntent
                                .getActivity(c, mPendingIntentId, mStartActivity,
                                        PendingIntent.FLAG_CANCEL_CURRENT);
                        AlarmManager mgr = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE);
                        mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
                        //kill the application
                        System.exit(0);
                    } else {
                        Log.e(TAG, "Was not able to restart application, mStartActivity null");
                    }
                } else {
                    Log.e(TAG, "Was not able to restart application, PM null");
                }
            } else {
                Log.e(TAG, "Was not able to restart application, Context null");
            }
        } catch (Exception ex) {
            Log.e(TAG, "Was not able to restart application");
        }
    }

Esto también reinicializará las clases jni y todas las instancias estáticas.

mikepenz avatar Mar 12 '2014 08:03 mikepenz

Jake Wharton publicó recientemente su biblioteca ProcessPhoenix , que hace esto de manera confiable. Básicamente sólo tienes que llamar:

ProcessPhoenix.triggerRebirth(context);

La biblioteca finalizará automáticamente la actividad de llamada, finalizará el proceso de la aplicación y luego reiniciará la actividad de la aplicación predeterminada.

TBieniek avatar Sep 04 '2015 12:09 TBieniek