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.

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?

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:

  1. Cuando inicia una aplicación a través de Eclipse o Market App, se inicia con indicadores de intención: FLAG_ACTIVITY_NEW_TASK.

  2. 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

  1. 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ó.
  2. 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" manifiesto android: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

  • Muestra la barra de progreso sin congelar la aplicación
  • Ocultar el teclado después de llamar a la nueva actividad que muestra un ProgressDialog
  • Preloading página web en Android (usando WebView?)
  • Cómo crear una actividad de configuración para Android Live Wallpaper
  • NativeActivity no termina
  • Android cómo crear mi propia actividad y ampliarla?
  • Finalizar todas las actividades anteriores
  • ¿Por qué `android: screenOrientation =" detrás "` no hay efecto en android 4.1.2?
  • Descripción de las transiciones de elementos compartidos exit / reenter
  • Circular personalizada revela resultados de transición en "java.lang.UnsupportedOperationException" cuando se pausan?
  • Cambiar la reentrada de animación a otro elemento de una lista
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.