Cómo evitar múltiples instancias de una actividad cuando se inicia con diferentes intenciones
Me he encontrado con un error en mi aplicación cuando se inicia con el botón "Abrir" en el mercado de Android. Parece que lanzarlo desde el mercado utiliza una intención diferente y luego lanzarlo desde el menú de aplicaciones del teléfono. Esto está llevando a múltiples copias de la misma actividad que se está lanzando, que están en conflicto entre sí.
Por ejemplo, si mi aplicación consiste en las actividades de ABC, entonces el problema anterior puede conducir a una pila ABCA.
- Animar ImageView entre dos actividades que utilizan transiciones de elemento compartidas con ChangeImageTransform
- BadTokenException ActivityGroup para TabActivity utilizando
- OnCreate siempre se llama si vuelve con intención
- Android: ¿Cómo iniciar la actividad desde el menú de preferencias?
- ¿Cómo liberar la memoria del objeto Activity real después de dejar la actividad?
He intentado usar android:launchMode="singleTask"
en todas las actividades para solucionar este problema, pero tiene el efecto secundario no deseado de borrar la pila de actividades a la raíz cada vez que golpeo HOME.
Ejemplo: ABC -> HOME -> A cuando lo que necesito es ABC -> HOME -> ABC
¿Existe una buena manera de evitar el lanzamiento de múltiples actividades del mismo tipo sin restablecer la actividad raíz cuando se usa HOME?
- Crash in Activity Transitions con SharedElement
- Problema extraño al realizar la transición de ImageView en Android 5.0
- Android: Cómo establecer programaticamente el tema de una actividad en Theme.Dialog
- Problemas para entender el ciclo de vida cuando la pantalla se apaga y se enciende
- Cómo comprobar si mi actividad es la actividad actual que se ejecuta en la pantalla
- ¿Se llamará a este método? (Todavía necesita una respuesta aceptada ... Ver detalles en la respuesta)
- Hipo en transiciones de actividad con elementos compartidos
- Notificación de llamada de BroadcastReceiver
Añadir esto a onCreate y usted debe ser bueno para ir:
// Possible work around for market launches. See http://code.google.com/p/android/issues/detail?id=2373 // for more details. Essentially, the market launches the main activity on top of other activities. // we never want this to happen. Instead, we check if we are the root and if not, we finish. if (!isTaskRoot()) { final Intent intent = getIntent(); if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(intent.getAction())) { Log.w(LOG_TAG, "Main Activity is not the root. Finishing Main Activity instead of launching."); finish(); return; } }
Sólo voy a explicar por qué falla, y cómo reproducir este error de forma programática para que pueda incorporar esto en su suite de pruebas:
-
Cuando inicia una aplicación a través de Eclipse o Market App, se inicia con indicadores de intención: FLAG_ACTIVITY_NEW_TASK.
-
Al iniciar a través del lanzador (inicio), utiliza banderas: FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_BROUGHT_TO_FRONT | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED, y utiliza la acción " MAIN " y la categoría " LAUNCHER ".
Si desea reproducir esto en un caso de prueba, siga estos pasos:
adb shell am start -f 0x10000000 -n com.testfairy.tests.regression.taskroot/.MainActivity
Luego haz lo que sea necesario para llegar a la otra actividad. Para mis propósitos, acabo de colocar un botón que inicia otra actividad. Luego, regrese al lanzador (casa) con:
adb shell am start -W -c android.intent.category.HOME -a android.intent.action.MAIN
Y simular el lanzamiento a través del lanzador con esto:
adb shell am start -a "android.intent.action.MAIN" -c "android.intent.category.LAUNCHER" -f 0x10600000 -n com.testfairy.tests.regression.taskroot/.MainActivity
Si no ha incorporado la solución isTaskRoot (), esto reproducirá el problema. Usamos esto en nuestras pruebas automáticas para asegurarnos de que este error nunca vuelva a ocurrir.
¡Espero que esto ayude!
¿Has probado el modo de inicio singleTop ?
Aquí está una parte de la descripción de http://developer.android.com/guide/topics/manifest/activity-element.html :
… una nueva instancia de una actividad "singleTop" también se puede crear para manejar una nueva intención. Sin embargo, si la tarea de destino ya tiene una instancia existente de la actividad en la parte superior de su pila, esa instancia recibirá la nueva intención (en una llamada onNewIntent ()); No se crea una instancia nueva. En otras circunstancias, por ejemplo, si una instancia existente de la actividad "singleTop" está en la tarea de destino, pero no en la parte superior de la pila, o si está en la parte superior de una pila, pero no en la tarea de destino Se creará una nueva instancia y se empujará en la pila.
Tal vez es esta cuestión ? O alguna otra forma del mismo error?
Creo que la respuesta aceptada ( Duane Homick ) tiene casos sin tratamiento :
Usted tiene diferentes extras (y duplicados de la aplicación como resultado):
- Cuando se inicia la aplicación desde el mercado o por el icono de la pantalla de inicio (que es colocado por Mercado automáticamente)
- Cuando inicia la aplicación por el lanzador o icono de pantalla de inicio creado manualmente
Aquí hay una solución (SDK_INT> = 11 para las notificaciones) que i belive manejar estos casos y notificaciones de la barra de estado también.
Manifiesto :
<activity android:name="com.acme.activity.LauncherActivity" android:noHistory="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <service android:name="com.acme.service.LauncherIntentService" />
Actividad del lanzador :
public static Integer lastLaunchTag = null; @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); mInflater = LayoutInflater.from(this); View mainView = null; mainView = mInflater.inflate(R.layout.act_launcher, null); // empty layout setContentView(mainView); if (getIntent() == null || getIntent().getExtras() == null || !getIntent().getExtras().containsKey(Consts.EXTRA_ACTIVITY_LAUNCH_FIX)) { Intent serviceIntent = new Intent(this, LauncherIntentService.class); if (getIntent() != null && getIntent().getExtras() != null) { serviceIntent.putExtras(getIntent().getExtras()); } lastLaunchTag = (int) (Math.random()*100000); serviceIntent.putExtra(Consts.EXTRA_ACTIVITY_LAUNCH_TAG, Integer.valueOf(lastLaunchTag)); startService(serviceIntent); finish(); return; } Intent intent = new Intent(this, SigninActivity.class); if (getIntent() != null && getIntent().getExtras() != null) { intent.putExtras(getIntent().getExtras()); } startActivity(intent); }
Servicio :
@Override protected void onHandleIntent(final Intent intent) { Bundle extras = intent.getExtras(); Integer lastLaunchTag = extras.getInt(Consts.EXTRA_ACTIVITY_LAUNCH_TAG); try { Long timeStart = new Date().getTime(); while (new Date().getTime() - timeStart < 100) { Thread.currentThread().sleep(25); if (!lastLaunchTag.equals(LauncherActivity.lastLaunchTag)) { break; } } Thread.currentThread().sleep(25); launch(intent); } catch (InterruptedException e) { e.printStackTrace(); } } private void launch(Intent intent) { Intent launchIintent = new Intent(LauncherIntentService.this, LauncherActivity.class); launchIintent.addCategory(Intent.CATEGORY_LAUNCHER); launchIintent.setAction(Intent.ACTION_MAIN); launchIintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); launchIintent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); if (intent != null && intent.getExtras() != null) { launchIintent.putExtras(intent.getExtras()); } launchIintent.putExtra(Consts.EXTRA_ACTIVITY_LAUNCH_FIX, true); startActivity(launchIintent); }
Notificación :
ComponentName actCN = new ComponentName(context.getPackageName(), LauncherActivity.class.getName()); Intent contentIntent = new Intent(context, LauncherActivity.class); contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= 11) { contentIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // if you need to recreate activity stack } contentIntent.addCategory(Intent.CATEGORY_LAUNCHER); contentIntent.setAction(Intent.ACTION_MAIN); contentIntent.putExtra(Consts.EXTRA_CUSTOM_DATA, true);
Me doy cuenta de que la pregunta no tiene nada que ver con Xamarin Android, pero quería publicar algo ya que no lo vi en ningún otro lugar.
Para solucionar esto en Xamarin Android he usado el código de @DuaneHomick y agregado a MainActivity.OnCreate()
. La diferencia con Xamarin es que debe ir después de Xamarin.Forms.Forms.Init(this, bundle);
Y LoadApplication(new App());
. Así que mi OnCreate()
se vería así:
protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); Xamarin.Forms.Forms.Init(this, bundle); LoadApplication(new App()); if(!IsTaskRoot) { Intent intent = Intent; string action = intent.Action; if(intent.HasCategory(Intent.CategoryLauncher) && action != null && action.Equals(Intent.ActionMain, System.StringComparison.OrdinalIgnoreCase)) { System.Console.WriteLine("\nIn APP.Droid.MainActivity.OnCreate() - Finishing Activity and returning since a second MainActivity has been created.\n"); Finish(); return; //Not necessary if there is no code below } } }
* Editar: Desde Android 6.0, la solución anterior no es suficiente para ciertas situaciones. Ahora también LaunchMode
puesto LaunchMode
a SingleTask
, que parece haber hecho que las cosas funcionen correctamente una vez más. No estoy seguro de los efectos que esto podría tener en otras cosas, por desgracia.
Tuve este problema también
- No llame a finish (); En la actividad doméstica se ejecutaría sin fin – la actividad en el hogar está siendo llamado por ActivityManager cuando terminó.
- Por lo general, cuando la configuración está cambiando (es decir, rotar la pantalla, cambiar el idioma, los cambios de servicio de telefonía, es decir, mcc mnc, etc) la actividad de recrear – y si la actividad en casa está en ejecución entonces llama de nuevo a A. para que la necesidad de añadir a
android:configChanges="mcc|mnc"
manifiestoandroid:configChanges="mcc|mnc"
– si tiene conexión a celular, consulte http://developer.android.com/guide/topics/manifest/activity-element.html#config para qué configuración hay al arrancar el sistema o pulsar Abierto o lo que sea.
Pruebe esta solución:
Crear clase de Application
y definir allí:
public static boolean IS_APP_RUNNING = false;
Luego en su primera Actividad (Lanzador) en onCreate
antes de setContentView(...)
agregue esto:
if (Controller.IS_APP_RUNNING == false) { Controller.IS_APP_RUNNING = true; setContentView(...) //Your onCreate code... } else finish();
PS Controller
es mi clase de Application
.
Tuve el mismo problema, y lo arreglé con la siguiente solución.
En su actividad principal agregue este código en la parte superior del método onCreate
:
ActivityManager manager = (ActivityManager) this.getSystemService( ACTIVITY_SERVICE ); List<RunningTaskInfo> tasks = manager.getRunningTasks(Integer.MAX_VALUE); for (RunningTaskInfo taskInfo : tasks) { if(taskInfo.baseActivity.getClassName().equals(<your package name>.<your class name>) && (taskInfo.numActivities > 1)){ finish(); } }
No olvide añadir este permiso en su manifiesto.
< uses-permission android:name="android.permission.GET_TASKS" />
Espero que te ayude
He encontrado una manera de evitar que comiencen las mismas actividades, esto funciona muy bien para mí
if ( !this.getClass().getSimpleName().equals("YourActivityClassName")) { start your activity }
Intente usar SingleInstance modo de lanzamiento con afinidad establecida en allowtaskreparenting Esto siempre creará la actividad en la nueva tarea pero también permitirá su reparenting. Compruebe esto: Atributo de afinidad
- Android: Añadir una vista de texto al diseño lineal mediante programación
- ¿Cómo puedo configurar mi aplicación de Android?