Cómo evitar instancias múltiples de una actividad cuando se lanza con diferentes intenciones

Me encontré con un error en mi aplicación cuando se lanzó con el botón “Abrir” en el mercado de Android. Parece que lanzarlo desde el mercado utiliza un propósito diferente y luego lanzarlo desde el menú de aplicaciones del teléfono. Esto está dando lugar a copias múltiples de la misma actividad que se inicia, que están en conflicto entre sí.

Por ejemplo, si mi aplicación consiste en las actividades ABC, el problema anterior puede llevar a una stack ABCA.

Intenté usar android:launchMode="singleTask" en todas las actividades para solucionar este problema, pero tiene el efecto secundario no deseado de borrar la stack de actividades para rootear cada vez que toco HOME.

Ejemplo: ABC -> INICIO -> A cuando lo que necesito es ABC -> INICIO -> ABC

¿Hay una buena manera de evitar el lanzamiento de actividades múltiples del mismo tipo sin reiniciar la actividad raíz al usar HOME?

Agregue esto a onCreate y debería estar listo para comenzar:

 // Possible work around for market launches. See https://issuetracker.google.com/issues/36907463 // 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; } } 

Voy a explicar por qué falla y cómo reproducir este error programáticamente para que pueda incorporar esto en su conjunto 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. Cuando se inicia a través del iniciador (inicio), usa indicadores: FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_BROUGHT_TO_FRONT | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED, y usa la acción ” PRINCIPAL ” y la categoría ” LANZADOR “.

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 (inicio) con:

 adb shell am start -W -c android.intent.category.HOME -a android.intent.action.MAIN 

Y simule lanzarlo a través del iniciador 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í hay algo de la descripción de http://developer.android.com/guide/topics/manifest/activity-element.html :

… también se puede crear una nueva instancia de una actividad “singleTop” para manejar una nueva intención. Sin embargo, si la tarea objective ya tiene una instancia existente de la actividad en la parte superior de su stack, esa instancia recibirá la nueva intención (en una llamada onNewIntent ()); una nueva instancia no se crea. En otras circunstancias, por ejemplo, si una instancia existente de la actividad “singleTop” está en la tarea objective, pero no en la parte superior de la stack, o si está en la parte superior de una stack, pero no en la tarea objective, una Se crearía una nueva instancia y se presionaría en la stack.

Tal vez es este problema ? ¿O alguna otra forma del mismo error?

Creo que la respuesta aceptada ( Duane Homick ) no se ha manejado :

Tiene diferentes extras (y duplicados de aplicaciones como resultado):

  • cuando inicia la aplicación desde Market o desde el ícono de la pantalla de inicio (que Market coloca automáticamente)
  • cuando inicia la aplicación mediante el iniciador o el icono de la pantalla de inicio creada manualmente

Aquí hay una solución (SDK_INT> = 11 para notificaciones) que creo manejar estos casos y las notificaciones de la barra de estado también.

Manifiesto

          

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 porque no lo vi en ningún otro lado.

Para solucionar esto en Xamarin Android, utilicé el código de @DuaneHomick y lo agregué a MainActivity.OnCreate() . La diferencia con Xamarin es que debe ir después de Xamarin.Forms.Forms.Init(this, bundle); y LoadApplication(new App()); . Entonces 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 configurado LaunchMode en SingleTask , que parece haber hecho que las cosas funcionen correctamente una vez más. Sin embargo, no estoy seguro de los efectos que esto podría tener en otras cosas, lamentablemente.

Tuve este problema también

  1. No llame a finish (); en la actividad hogareña se ejecutaría interminablemente: el ActivityManager llama a la actividad hogareña cuando finaliza.
  2. Por lo general, cuando la configuración cambia (es decir, rota la pantalla, cambia de idioma, cambia el servicio de telefonía, es decir, mcc mnc, etc.), la actividad se recrea; y si la actividad en el hogar se está ejecutando, vuelve a llamar a A. para agregar android:configChanges="mcc|mnc" – si tiene conexión a celular, consulte http://developer.android.com/guide/topics/manifest/activity-element.html#config para saber qué configuración hay al arrancar el sistema o presionar abierto o lo que sea.

Prueba esta solución:
Crea una clase de Application y define allí:

 public static boolean IS_APP_RUNNING = false; 

Luego, en su primera actividad ( onCreate ) en onCreate before setContentView(...) agregue esto:

 if (Controller.IS_APP_RUNNING == false) { Controller.IS_APP_RUNNING = true; setContentView(...) //Your onCreate code... } else finish(); 

El Controller PS es mi clase de Application .

Tuve el mismo problema y lo solucioné 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 tasks = manager.getRunningTasks(Integer.MAX_VALUE); for (RunningTaskInfo taskInfo : tasks) { if(taskInfo.baseActivity.getClassName().equals(.) && (taskInfo.numActivities > 1)){ finish(); } } 

no olvides agregar este permiso en tu manifiesto.

 < uses-permission android:name="android.permission.GET_TASKS" /> 

espero que te ayude

Encontré una manera de evitar comenzar las mismas actividades, esto funciona muy bien para mí

 if ( !this.getClass().getSimpleName().equals("YourActivityClassName")) { start your activity } 

intente usar el modo de inicio de SingleInstance con afinidad establecida para allowtaskreparenting Esto siempre creará la actividad en una nueva tarea pero también permitirá su reparación. Check dis: atributo de afinidad

    Intereting Posts