¿Cómo “reinicio” programáticamente una aplicación de Android?

En primer lugar, sé que uno realmente no debería matar / 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 una información específica al cliente.

El usuario solo 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 locking de “inicio de sesión”, todas las demás instancias de ese usuario tendrán que eliminar sus datos (restablecimiento de fábrica) para mantener la coherencia.

Es posible obtener el locking a la fuerza, ya que el usuario podría eliminar la aplicación y volver a instalarla, lo que daría como resultado una ID de instancia diferente y el usuario ya no podría liberar el locking. Por lo tanto, es posible obtener el locking a la fuerza.

Debido a esa posibilidad de fuerza, necesitamos verificar siempre en una instancia concreta que tiene el locking. Eso se hace en (casi) cada solicitud al servidor. El servidor podría enviar un “ID de locking erróneo”. Si eso se detecta, la aplicación cliente debe eliminar todo.


Ese fue el caso de uso. No por la pregunta de implementación:

Tengo una Activity A que inicia la Activity inicio de sesión L o la Activity principal B de la aplicación, dependiendo de un campo de Preferencias compartidas. Después de iniciar L o B, se cierra solo para que L o B se estén ejecutando. Entonces, en el caso de que el usuario ya haya iniciado sesión, B se está ejecutando ahora.

B inicia C. C llama a startService para IntentService D. Esto da como resultado esta stack:

(A)> B> C> D

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

R ahora maneja ese evento al proporcionarle al usuario un cuadro de diálogo en el que puede elegir reiniciar de fábrica la aplicación (eliminar la base de datos, los Prefs compartidos, etc.)

Después del restablecimiento de fábrica, quiero reiniciar la aplicación (para cerrar todas las actividades) y volver a iniciar A, que luego inicia la Activity L de inicio de sesión y finaliza:

(A)> L

El método OnClick de Dialog 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 clase MyApp :

 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 ahora es que si uso FLAG_ACTIVITY_NEW_TASK las actividades B y C siguen ejecutándose. Si presiono el botón Atrás en la Activity inicio de sesión, veo C, pero quiero volver a la pantalla de inicio.

Si no configuro FLAG_ACTIVITY_NEW_TASK , FLAG_ACTIVITY_NEW_TASK 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 el Context Actividades, porque el ServiceIntent D también puede ser llamado desde una tarea en segundo plano que inicia el AlarmManager .

Entonces, ¿cómo podría resolver esto en la stack de actividades convirtiéndose en (A)> L?

Puede usar PendingIntent para configurar el inicio de su actividad de inicio en el futuro y luego cierre 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); 

Simplemente puede 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 usa en la biblioteca ProcessPhoenix


Como alternativa:

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

Si realmente desea reiniciar su actividad, incluida la eliminació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 packagemnager 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 reiniciará las clases jni y todas las instancias estáticas.

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

 ProcessPhoenix.triggerRebirth(context); 

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

IntentCompat.makeRestartActivityTask

La nueva forma de hacerlo es mediante el uso de IntentCompat.makeRestartActivityTask

Haga un Intento que pueda usarse para relanzar la tarea de una aplicación en su estado base. Esto es como makeMainActivity (ComponentName), pero también establece las banderas Intent.FLAG_ACTIVITY_NEW_TASK y FLAG_ACTIVITY_CLEAR_TASK.

 PackageManager packageManager = context.getPackageManager(); Intent intent = packageManager.getLaunchIntentForPackage(context.getPackageName()); ComponentName componentName = intent.getComponent(); Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName); context.startActivity(mainIntent); System.exit(0); 

Hay un buen truco. Mi problema era que alguna biblioteca C ++ jni muy antigua había filtrado recursos. En algún momento, dejó de funcionar. El usuario intentó salir de la aplicación y ejecutarla de nuevo, sin resultado, porque finalizar una actividad no es lo mismo que finalizar (o matar) el proceso. (Por cierto, el usuario podría ir a la lista de las aplicaciones en ejecución y detenerlo desde allí; esto funcionaría, pero los usuarios simplemente no saben cómo finalizar las aplicaciones).

Si desea observar el efecto de esta característica, agregue una variable static a su actividad y auméntela cada, por ejemplo, presione el botón. Si sale de la actividad de la aplicación y luego vuelve a invocar la aplicación, esta variable estática mantendrá su valor. (Si realmente se salió de la aplicación, a la variable se le asignará el valor inicial).

(Y tengo que comentar por qué no quería arreglar el error en su lugar. La biblioteca fue escrita hace décadas y se filtraron recursos desde entonces. La gerencia cree que siempre funcionó . El costo de proporcionar una solución en lugar de una solución alternativa … Creo que entiendes la idea).

Ahora, ¿cómo podría restablecer una biblioteca jni shared (aka dynamic, .so) al estado inicial? Elegí reiniciar la aplicación como un nuevo proceso.

El truco es que System.exit () cierra la actividad actual y Android recrea la aplicación con una actividad menos.

Entonces el código es:

 /** This activity shows nothing; instead, it restarts the android process */ public class MagicAppRestart extends Activity { // Do not forget to add it to AndroidManifest.xml //  @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); System.exit(0); } public static void doRestart(Activity anyActivity) { anyActivity.startActivity(new Intent(anyActivity.getApplicationContext(), MagicAppRestart.class)); } } 

La actividad de llamada solo ejecuta el código MagicAppRestart.doRestart(this); , se ejecuta onPause() la actividad de onPause() y luego se vuelve a crear el proceso. Y no olvide mencionar esta actividad en AndroidManifest.xml

La ventaja de este método es que no hay retrasos.

UPD: funcionó en Android 2.x, pero en Android 4 algo ha cambiado.

Ok, refactoreé mi aplicación y no terminaré A automáticamente. onActivityResult que esto se ejecute siempre y lo termino a través del evento onActivityResult . De esta forma, puedo usar los FLAG_ACTIVITY_CLEAR_TOP + FLAG_ACTIVITY_NEW_TASK para obtener lo que quiero:

 public class A extends Activity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); finish(); } protected void onResume() { super.onResume(); // ... if (loggedIn) { startActivityForResult(new Intent(this, MainActivity.class), 0); } else { startActivityForResult(new Intent(this, LoginActivity.class), 0); } } } 

y en el ResultReceiver

 @Override public void onClick(DialogInterface dialog, int which) { MyApp.factoryReset(); 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); } 

¡Gracias de cualquier manera!

 Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage( getBaseContext().getPackageName() ); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); 

Mi solución no reinicia el proceso / aplicación. Solo permite que la aplicación “reinicie” la actividad hogareña (y descarta todas las demás actividades). Parece un reinicio para los usuarios, pero el proceso es el mismo. Creo que en algunos casos la gente quiere lograr este efecto, así que lo dejo aquí para tu información.

 public void restart(){ Intent intent = new Intent(this, YourHomeActivity.class); this.startActivity(intent); this.finishAffinity(); } 

He modificado ligeramente la respuesta de Ilya_Gazman para usar nuevas API (IntentCompat está en desuso al iniciar 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); context.startActivity(mainIntent); Runtime.getRuntime().exit(0); } 

La mejor forma de reiniciar completamente una aplicación es reiniciarla, no solo para saltar a una actividad con FLAG_ACTIVITY_CLEAR_TOP y FLAG_ACTIVITY_NEW_TASK . Así que mi solución es hacerlo desde su aplicación o incluso desde otra aplicación, la única condición es conocer el nombre del paquete de la aplicación (ejemplo: ‘ com.example.myProject ‘)

  public static void forceRunApp(Context context, String packageApp){ Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageApp); launchIntent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(launchIntent); } 

Ejemplo de reinicio de uso o inicio de appA desde appB :

 forceRunApp(mContext, "com.example.myProject.appA"); 

Puede verificar si la aplicación se está ejecutando:

  public static boolean isAppRunning(Context context, String packageApp){ ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List procInfos = activityManager.getRunningAppProcesses(); for (int i = 0; i < procInfos.size(); i++) { if (procInfos.get(i).processName.equals(packageApp)) { return true; } } return false; } 

Nota : Sé que esta respuesta está un poco fuera de tema, pero puede ser realmente útil para alguien.

Intenta usar FLAG_ACTIVITY_CLEAR_TASK

Aquí hay un ejemplo para reiniciar su aplicación de forma genérica utilizando el PackageManager:

 Intent i = getBaseContext().getPackageManager() .getLaunchIntentForPackage( getBaseContext().getPackageName() ); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); 

El único código que no activó “Tu aplicación se cerró inesperadamente” es la siguiente. También es un código no desaprobado que no requiere una biblioteca externa. Tampoco requiere un temporizador.

 public static void triggerRebirth(Context context, Class myClass) { Intent intent = new Intent(context, myClass); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); context.startActivity(intent); Runtime.getRuntime().exit(0); } 

FLAG_ACTIVITY_CLEAR_TASK directamente la pantalla inicial con FLAG_ACTIVITY_CLEAR_TASK y FLAG_ACTIVITY_NEW_TASK .

Tuve que agregar un controlador para retrasar la salida:

  mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 200, mPendingIntent); final Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { Runtime.getRuntime().exit(0); } }, 100); 

Puede usar el método de la startInstrumentation de Activity . Necesitas implementar Instrumentation vacía y señalar el manifiesto. Después de eso puedes llamar a este método para reiniciar tu aplicación. Me gusta esto:

 try { InstrumentationInfo info = getPackageManager().queryInstrumentation(getPackageName(), 0).get(0); ComponentName component = new ComponentName(this, Class.forName(info.name)); startInstrumentation(component, null, null); } catch (Throwable e) { new RuntimeException("Failed restart with Instrumentation", e); } 

Obtengo el nombre de la clase de Instrumentación dinámicamente pero puedes codificarlo. Algo como esto:

 try { startInstrumentation(new ComponentName(this, RebootInstrumentation.class), null, null); } catch (Throwable e) { new RuntimeException("Failed restart with Instrumentation", e); } 

Llame a startInstrumentation que vuelva a cargar su aplicación. Lea la descripción de este método. Pero no puede ser seguro si actúas como matar a la aplicación.

La aplicación en la que estoy trabajando tiene que dar al usuario la posibilidad de elegir qué fragmentos mostrar (los fragmentos se cambian dinámicamente en el tiempo de ejecución). La mejor solución para mí fue reiniciar completamente la aplicación.

Así que probé muchas soluciones y ninguna de ellas me funcionó, pero esto:

 final Intent mStartActivity = new Intent(SettingsActivity.this, Splash.class); final int mPendingIntentId = 123456; final PendingIntent mPendingIntent = PendingIntent.getActivity(SettingsActivity.this, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT); final AlarmManager mgr = (AlarmManager) SettingsActivity.this.getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); this.finishAffinity(); //notice here Runtime.getRuntime().exit(0); //notice here 

¡Esperando que eso ayude a alguien más!

prueba esto:

 Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); 

Utilizar:

 navigateUpTo(new Intent(this, MainActivity.class)); 

Funciona a partir del nivel API 16 (4.1), creo.

Puede reiniciar su actividad actual de esta manera:

Fragmento :

 activity?.recreate() 

Actividad :

 recreate()