pie - android versiones
¿Cómo "reinicio" programáticamente una aplicación de Android? (19)
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);
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 bloqueo 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 bloqueo 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 bloqueo. Por lo tanto, es posible obtener el bloqueo a la fuerza.
Debido a esa posibilidad de fuerza, necesitamos verificar siempre en una instancia concreta que tiene el bloqueo. Eso se hace en (casi) cada solicitud al servidor. El servidor podría enviar un "ID de bloqueo 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 pila:
(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 pila de actividades convirtiéndose en (A)> L?
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);
}
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
// <activity android:name="your.package.name.MagicAppRestart"/>
@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.
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);
}
Intenta usar FLAG_ACTIVITY_CLEAR_TASK
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.
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!
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<ActivityManager.RunningAppProcessInfo> 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.
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();
}
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!
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);
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.
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);
Utilizar:
navigateUpTo(new Intent(this, MainActivity.class));
Funciona a partir del nivel API 16 (4.1), creo.
prueba esto:
Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
FLAG_ACTIVITY_CLEAR_TASK
directamente la pantalla inicial con FLAG_ACTIVITY_CLEAR_TASK
y FLAG_ACTIVITY_NEW_TASK
.
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 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 reiniciará las clases jni y todas las instancias estáticas.
Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage( getBaseContext().getPackageName() );
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);