La aplicación se reinicia en lugar de reanudarse

194

Espero que alguien pueda ayudarme a descubrir, si no una solución, al menos una explicación de un comportamiento.

El problema:

En algunos dispositivos, presionar el ícono del iniciador hace que se reanude la tarea actual, en otros resulta en que se dispare la intención de inicio inicial (reiniciando efectivamente la aplicación). ¿Por qué pasó esto?

El detalle:

Cuando presiona el "Icono del iniciador", la aplicación se inicia normalmente: es decir, supongo que se inicia una intención con el nombre de su primero Activitycon la acción android.intent.action.MAINy la categoría android.intent.category.LAUNCHER. Sin embargo, este no siempre puede ser el caso:

En la mayoría de los dispositivos, si presiona el ícono del iniciador después de que la aplicación ya se esté ejecutando, la Actividad actualmente en ejecución en ese proceso se reanudará ( NO la inicial Activity). Se reanuda de la misma manera que si lo hubiera seleccionado en "Tareas recientes" en el menú del sistema operativo. Este es el comportamiento que quiero en todos los dispositivos.

Sin embargo, en otros dispositivos seleccionados se produce un comportamiento diferente:

  • En el Motorola Xoom, cuando presiona el ícono del iniciador, la aplicación siempre iniciará el inicio inicial Activityindependientemente de lo que se esté ejecutando actualmente. Supongo que los íconos del iniciador siempre inician la intención "LANZADOR".

  • En el Samsung Tab 2, cuando presiona el ícono del iniciador, si acaba de instalar la aplicación, siempre iniciará la inicial Activity(igual que el Xoom); sin embargo, después de reiniciar el dispositivo después de la instalación, el ícono del iniciador en su lugar reanudar la aplicación ¿Supongo que estos dispositivos agregan "aplicaciones instaladas" en una tabla de búsqueda en el inicio del dispositivo que permite que los íconos del iniciador reanuden correctamente las tareas en ejecución?

He leído muchas respuesta que el sonido similar a mi problema, sino simplemente añadiendo android:alwaysRetainTaskState="true"o el uso de launchMode="singleTop"la Activityno son la respuesta.

Editar:

Después del lanzamiento más reciente de esta aplicación, encontramos que este comportamiento ha comenzado a ocurrir en todos los dispositivos después del primer reinicio. Lo que me parece una locura, pero mirando a través del proceso de reinicio, en realidad no puedo encontrar lo que está mal.

Graeme
fuente
1
Puede parecer una pregunta trivial, pero ¿estableció "No mantener actividades" como verdadero en sus opciones de desarrollo para Xoom?
Andrew Schuster
No (¡deseo! :)): he registrado el ciclo de vida de cada actividad y las actividades en segundo plano como todavía disponibles (están detenidas, no destruidas). El sistema operativo parece recurrir finish()a ellos en los casos en que comienza el primero de Activitynuevo en lugar de reanudarlos.
Graeme
1
Si ha presionado el botón de inicio y luego hace clic en el icono del iniciador, el comportamiento de reanudar es el predeterminado para Android, como probablemente ya sabe. Sin embargo, si presiona el botón Atrás para volver a la pantalla de inicio, la mayoría de los teléfonos finalizarán () la aplicación. ¿Es posible que el método que esté utilizando para salir de la aplicación sea diferente en los diferentes dispositivos? ¿Podrías cerrar sesión en onKeyUpEvent para comprobar que algunos no manejan los tkeys de hardware / software de forma extraña?
Nick Cardoso
2
No, estoy seguro del problema como se indicó anteriormente. Usando la página de inicio para poner la aplicación en segundo plano (no atrás, lo que tienes razón terminaría () la Actividad). En Xoom es posible reanudar la aplicación desde la Lista de tareas (solo que no desde el Iniciador), por lo que definitivamente no se ha eliminado el backstack .
Graeme
1
Responder con la recompensa es la forma de solucionar el problema descrito en la pregunta. Marqué mi propia respuesta como "correcta" porque aunque a veces el problema es causado por un error de aplicación en el iniciador (como se indica en su respuesta), mi problema particular fue causado por el cambio de Tarea. La solución a ambos problemas está arreglada por su solución.
Graeme

Respuestas:

238

El comportamiento que está experimentando es causado por un problema que existe en algunos lanzadores de Android desde la API 1. Puede encontrar detalles sobre el error y las posibles soluciones aquí: https://code.google.com/p/android/issues/ detalle? id = 2373 .

Es un problema relativamente común en los dispositivos Samsung, así como en otros fabricantes que usan un iniciador / máscara personalizado. No he visto que el problema ocurra en un iniciador de Android.

Básicamente, la aplicación no se está reiniciando por completo, pero su actividad de inicio se está iniciando y agregando a la parte superior de la pila de actividades cuando el iniciador reanuda la aplicación. Puede confirmar que este es el caso haciendo clic en el botón Atrás cuando reanude la aplicación y se muestre la Actividad de inicio. Luego debería ser llevado a la Actividad que esperaba que se mostrara cuando reanudó la aplicación.

La solución que elegí implementar para resolver este problema es verificar la categoría Intent.CATEGORY_LAUNCHER y la acción Intent.ACTION_MAIN en el intento que inicia la Actividad inicial. Si esos dos indicadores están presentes y la Actividad no está en la raíz de la tarea (lo que significa que la aplicación ya se estaba ejecutando), entonces llamo a terminar () en la Actividad inicial. Es posible que esa solución exacta no funcione para usted, pero algo similar debería funcionar.

Esto es lo que hago en onCreate () de la Actividad inicial / de lanzamiento:

    if (!isTaskRoot()
            && getIntent().hasCategory(Intent.CATEGORY_LAUNCHER)
            && getIntent().getAction() != null
            && getIntent().getAction().equals(Intent.ACTION_MAIN)) {

        finish();
        return;
    }
starkej2
fuente
44
Hasta ahora, esto funciona para mí sin efectos secundarios adversos hasta ahora. Basado en los supuestos lógicos, no veo ninguna razón por la cual no sea válido.
javahead76
3
Creo que esta es la forma correcta de lidiar con este error. Funciona para mi.
Sokolov
3
hizo el trabajo por mí, verificado en unos 8 dispositivos diferentes. ¡muchas gracias!
shaya ajzner
3
WOOOOOW solucionó mi problema que he estado buscando una solución durante las últimas 2 horas
Jean Raymond Daher
2
Gracias @ starkej2. Trabajado como un encanto.
Rajeev Sahu
54

Esta pregunta aún es relevante en 2016. Hoy, un probador de control de calidad informó que una aplicación mía se reiniciaba en lugar de reanudarse desde el iniciador de stock en Android M.

En realidad, el sistema estaba agregando la actividad iniciada a la pila de tareas actual , pero al usuario le pareció que había ocurrido un reinicio y que había perdido su trabajo. La secuencia fue:

  1. Descargar desde play store (o sideload apk)
  2. Iniciar aplicación desde el cuadro de diálogo de Play Store: aparece la actividad A [pila de tareas: A]
  3. Navegue a la actividad B [pila de tareas: A -> B]
  4. Presione el botón 'Inicio'
  5. Inicie la aplicación desde el cajón de aplicaciones: ¡aparece la actividad A! [pila de tareas: A -> B -> A] (el usuario podría presionar el botón 'Atrás' para acceder a la actividad 'B' desde aquí)

Nota: este problema no se manifiesta para los APK de depuración implementados a través de ADB, solo en los APK descargados de Play Store o de carga lateral. En los últimos casos, la intención de lanzamiento del paso 5 contenía la bandera Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT, pero no en los casos de depuración. El problema desaparece una vez que la aplicación se ha iniciado en frío desde el iniciador. Mi sospecha es que la Tarea se siembra con un Intento malformado (más exactamente, no estándar) que evita el comportamiento de inicio correcto hasta que la tarea se borre por completo.

Probé varios modos de inicio de actividad , pero esa configuración se desvía demasiado del comportamiento estándar que el usuario esperaría: reanudar la tarea en la actividad B. Consulte la siguiente definición de comportamiento esperado en la guía Tareas y Pila de documentos , en la parte inferior de la página bajo 'Inicio de una tarea':

Un filtro de intención de este tipo hace que se muestre un icono y una etiqueta para la actividad en el iniciador de aplicaciones, lo que brinda a los usuarios una forma de iniciar la actividad y volver a la tarea que crea en cualquier momento después de que se haya iniciado.

Encontré que esta respuesta era relevante e inserté lo siguiente en el método 'onCreate' de mi actividad raíz (A) para que se reanude adecuadamente cuando el usuario abre la aplicación.

                    /**
     * Ensure the application resumes whatever task the user was performing the last time
     * they opened the app from the launcher. It would be preferable to configure this
     * behavior in  AndroidMananifest.xml activity settings, but those settings cause drastic
     * undesirable changes to the way the app opens: singleTask closes ALL other activities
     * in the task every time and alwaysRetainTaskState doesn't cover this case, incredibly.
     *
     * The problem happens when the user first installs and opens the app from
     * the play store or sideloaded apk (not via ADB). On this first run, if the user opens
     * activity B from activity A, presses 'home' and then navigates back to the app via the
     * launcher, they'd expect to see activity B. Instead they're shown activity A.
     *
     * The best solution is to close this activity if it isn't the task root.
     *
     */

    if (!isTaskRoot()) {
        finish();
        return;
    }

ACTUALIZACIÓN: alejó esta solución de analizar indicadores de intención a consultar si la actividad está directamente en la raíz de la tarea. Los indicadores de intención son difíciles de predecir y probar con todas las diferentes formas de abrir una actividad PRINCIPAL (Iniciar desde casa, iniciar desde el botón 'arriba', iniciar desde Play Store, etc.)

Rich Ehmer
fuente
44
"El problema desaparece una vez que la aplicación se inició en frío desde el iniciador". Esta es la parte más extraña y también la observé: al matar la aplicación después del primer lanzamiento, comienza a comportarse normalmente de nuevo. Un error tan extraño. Gracias por analizarlo tan a fondo y por la solución.
Oded
11
Nota: puede volver a reproducir el problema después de borrarlo inicialmente ("iniciando en frío" como lo describió) usando "Abrir" desde la página de Google Play de la aplicación, incluso si instaló el APK a través de Android Studio . Encontré esto muy útil para verificar que la solución funcionó.
Oded
Buena explicación!
karanatwal.github.io
Gracias por esta explicación :)
AndroidEnthusiast
2
Esto sucede con muchas de las aplicaciones. Google Photos es uno de los principales que probé.
Raghubansh Mani
19

¡Ajá! (tldr; Vea las declaraciones en negrita en la parte inferior)

He encontrado el problema ... creo.

Entonces, comenzaré con una suposición. Cuando presiona el iniciador , se inicia el valor predeterminado Activityo, si Taskestá abierto un inicio anterior, lo pone al frente. Dicho de otra manera: si en cualquier etapa de su navegación crea una nueva Tasky finishla anterior, el iniciador ya no reanudará su aplicación.

Si esa suposición es cierta, estoy bastante seguro de que debería ser un error, dado que cada uno Taskestá en el mismo proceso y es tan válido como candidato para el currículum vitae.

Entonces, mi problema se solucionó eliminando estas banderas de un par de Intents:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK );

Si bien es bastante obvio que FLAG_ACTIVITY_NEW_TASKcrea una nueva Task, no me gustó que la suposición anterior estuviera vigente. Lo consideré un culpable y lo eliminé para probarlo y todavía tenía un problema, así que lo descarté. Sin embargo, todavía tenía las siguientes condiciones:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)

Mi pantalla de inicio estaba iniciando el "principal" Activityen mi aplicación usando la bandera anterior. Después de todo, si hubiera "reiniciado" mi aplicación y Activitytodavía estuviera ejecutándose, preferiría preservar su información de estado.

Notarás en la documentación que no hace mención de comenzar un nuevo Task:

Si se establece, y la actividad que se está iniciando ya se está ejecutando en la tarea actual, en lugar de iniciar una nueva instancia de esa actividad, todas las demás actividades se cerrarán y esta intención se entregará al (ahora en arriba) actividad anterior como una nueva intención.

Por ejemplo, considere una tarea que consta de las actividades: A, B, C, D. Si D llama a startActivity () con un Intento que resuelve el componente de la actividad B, entonces C y D finalizarán y B recibirá el Intento dado , dando como resultado que la pila ahora sea: A, B.

La instancia actualmente en ejecución de la actividad B en el ejemplo anterior recibirá la nueva intención que está comenzando aquí en su método onNewIntent (), o se terminará y reiniciará con la nueva intención. Si ha declarado que su modo de inicio es "múltiple" (el valor predeterminado) y no ha configurado FLAG_ACTIVITY_SINGLE_TOP en la misma intención, se finalizará y se volverá a crear; para todos los demás modos de inicio o si FLAG_ACTIVITY_SINGLE_TOP está configurado, esta intención se entregará a la instancia actual onNewIntent ().

Este modo de inicio también se puede usar con buenos resultados junto con FLAG_ACTIVITY_NEW_TASK: si se usa para iniciar la actividad raíz de una tarea, pondrá en primer plano cualquier instancia de esa tarea que se esté ejecutando actualmente y luego la borrará a su estado raíz. Esto es especialmente útil, por ejemplo, al iniciar una actividad desde el administrador de notificaciones.

Entonces, tuve la situación como se describe a continuación:

  • Alanzado Bcon FLAG_ACTIVITY_CLEAR_TOP, Aacabados.
  • Bdesea reiniciar un servicio, de modo que envía al usuario Aque tiene la lógica de reinicio del servicio y la interfaz de usuario (sin banderas).
  • Ase inicia Bcon FLAG_ACTIVITY_CLEAR_TOP, Atermina.

En esta etapa FLAG_ACTIVITY_CLEAR_TOP, se reinicia el segundo indicador Bque se encuentra en la pila de tareas. Supongo que esto debe destruir el Tasky comenzar uno nuevo, causando mi problema, que es una situación muy difícil de detectar si me preguntas.

Entonces, si todas mis suposiciones son correctas:

  • El Launcherúnico reanuda la tarea creada inicialmente
  • FLAG_ACTIVITY_CLEAR_TOPSi reinicia el único restante Activity, también recreará un nuevoTask
Graeme
fuente
FLAG_ACTIVITY_CLEAR_TOP no crea una nueva tarea ni reinicia la actividad que está intentando iniciar si es la única actividad restante. "Si está configurado, y la actividad que se está iniciando ya se está ejecutando en la tarea actual, entonces, en lugar de lanzar una nueva instancia de esa actividad, todas las demás actividades se cerrarán y esta intención se entregará al (ahora arriba) antigua actividad como una nueva intención ".
starkej2
2
Me doy cuenta de que no está destinado a hacerlo, pero a través de prueba y error, este es el caso en mi caso. Eliminar estas banderas soluciona el problema.
Graeme
Esto no crea un currículum perfecto en todos los dispositivos en todas las condiciones.
danny117
Eliminar las banderas me solucionó el problema. Gracias
Sealer_05
Tengo un escenario de pantalla principal / pantalla principal pero no estoy usando ningún indicador para pasar de la pantalla principal a la principal, aún así el problema es reproducible para mí: esta solución no me funciona.
ROR
12

Tuve el mismo problema en los dispositivos Samsung. Después de buscar mucho, ninguna de estas respuestas funcionó para mí. Encontré eso en el archivo AndroidManifest.xml , launchModeestá configurado en singleInstance( android:launchMode="singleInstance"). Eliminar el launchModeatributo solucionó mi problema.

Ali
fuente
De hecho, esto también me sirvió. Esta respuesta de otra pregunta SO me pareció útil: stackoverflow.com/a/21622266/293280 . Y esto escribe sobre los diferentes tipos de launchModevalores: inthecheesefactory.com/blog/…
Joshua Pinter
¡Esta solución funcionó para mí! Se agregó esto no en manifiesto sino en atributo de actividad sobre la clase de actividad principal.
Calin Vlasin
@CalinVlasin, ¿podría mostrarme exactamente cómo utilizó el launchMode? donde lo colocaste actualmente lo tengo así, pero está causando el problema: <actividad android: name = ". UI.landing.MyActivity" android: configChanges = "locale | layoutDirection" android: launchMode = "singleTop" android: windowSoftInputMode = "stateAlwaysHidden | ajustarResize ">
j2emanue
Este fue mi problema también. Creo que esto debería usarse junto con la respuesta aceptada (! IsTaskRoot ...
behelit
1

En mi Cat s60, había habilitado "No mantener actividades" en las opciones de desarrollador, deshabilitar esto nuevamente me permitió cambiar de aplicación sin perder el estado de las aplicaciones ...

ptp
fuente
No tengo idea de cómo, pero esto estaba activado en mi dispositivo.
realPro
0

Esta solución funcionó para mí:

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            Intent startMain = new Intent(Intent.ACTION_MAIN);
            startMain.addCategory(Intent.CATEGORY_HOME);
            startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(startMain);
            return false;
        }
        else
            return super.onKeyUp(keyCode, event);
    }

crédito: necesito minimizar la aplicación de Android al hacer clic en el botón Atrás

Es posible que no funcione en todos los dispositivos, pero crea correctamente el comportamiento del botón de inicio cuando se presiona el botón Atrás, lo que detiene la actividad en lugar de finalizarla.

Russell Chisholm
fuente
Truco interesante, pero no resuelve el problema como se indicó. Muy útil para otros problemas / preguntas sin embargo.
Graeme
El botón Atrás tiene un propósito específico y los usuarios esperan que el botón Atrás haga lo que se supone que debe hacer. Anularlo de alguna manera está mal y, en mi opinión, es muy poco profesional.
errores suceden el
-1

Tuve el mismo problema, la causa fue:

(Código de Kotlin, en MainActivity)

override fun onBackPressed() {
    finish()
}

Entonces, cuando navego a mi MainActivity desde mi LoginActivity, uso esto:

    val intent = Intent(this, MainActivity::class.java)
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    startActivity(intent)

Al usar estos indicadores, no tengo que tener un onBackPressed () en mi MainActivity, saldrá de la aplicación de forma natural al hacer clic de nuevo. Y al presionar el botón Inicio y volver a la aplicación, no se reinicia.

Christer
fuente
-2

Solución para las personas que no tienen idea de programar y experimentar este problema en su teléfono Android. Esto sucede principalmente debido a la actualización de la versión de Android (solo mi suposición). Después de la actualización, todas sus aplicaciones se optimizan para usar menos batería. Pero, esto a su vez ralentiza su dispositivo.

Cómo resolver

Vaya a configuración >> Aplicaciones >> configuración de aplicaciones (busque el signo de configuración en cualquier lugar de la pantalla, es diferente en diferentes dispositivos) >> optimización de la batería (u opti similar [ingrese la descripción de la imagen aquí] [1] encendido) >> mueva todo aplicaciones en estado 'no optimizado' (tienen que hacer 1 por 1 manualmente, se puede permitir / no permitir en algunos teléfonos). Su aplicación de inicio debe estar 'no optimizada' (lanzador Zen UI en mi caso, supongo que es el culpable, podría intentar optimizar / No optimizar y reiniciar diferentes aplicaciones si tiene tiempo). Ahora reinicia tu teléfono.(no es necesario restablecer los datos / modo seguro ni ningún problema)

Prueba la multitarea ahora. :) Al presionar el ícono del iniciador ahora se debe reanudar la tarea actual. :) Su dispositivo se convertirá No se preocupe por la batería, se agotará de todos modos.

Arun Antony
fuente
-8

No tiene precio para sus usuarios. El currículum perfecto incluso después de estar sentado semanas en la lista de aplicaciones utilizadas recientemente.

Parece un currículum para el usuario, pero en realidad es un comienzo completo.

Antecedentes: la memoria utilizada por las aplicaciones que se encuentran en la actividad principal que no han iniciado una tarea es fácil de recuperar. El sistema operativo simplemente puede reiniciar la aplicación con el paquete original pasado a onCreate. Sin embargo, puede agregar al paquete original para que cuando el sistema operativo reinicie su aplicación, pueda restaurar el estado de la instancia y nadie sea más sabio sobre si la aplicación se reinició o se reanudó. Tomemos, por ejemplo, el programa de mapas clásico. El usuario se mueve a una posición en el mapa y luego presiona la tecla de inicio. Dos semanas después, esta aplicación de mapeo todavía está en la lista de aplicaciones recientes junto con Facebook, Pandora y Candy Crush. El sistema operativo no solo guarda el nombre de la aplicación para las aplicaciones utilizadas recientemente, sino que también guarda el paquete original utilizado para iniciar la aplicación. Sin embargo, el programador ha codificado el método para que el paquete original ahora contenga todos los materiales e información necesarios para construir la aplicación para que parezca que se reanudó.onSaveInstanceStateonSaveInstanceState

Ejemplo: guarde la posición actual de la cámara en onSaveInstanceState solo en caso de que la aplicación esté descargada y deba reiniciarse semanas después de la lista de aplicaciones recientes.

@Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        // save the current camera position;
        if (mMap != null) {
            savedInstanceState.putParcelable(CAMERA_POSITION,
                    mMap.getCameraPosition());
        }
    }



@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // get the exact camera position if the app was unloaded.
        if (savedInstanceState != null) {
            // get the current camera position;
            currentCameraPosition = savedInstanceState
                    .getParcelable(CAMERA_POSITION);
        }

Nota: también puede usar el onRestoreInstanceStatemétodo, pero me resulta más fácil restaurar la instancia en onCreate.

Esto es más que probable lo que está sucediendo en su aplicación. En algunos dispositivos, su aplicación se descarga a la memoria libre. Sí, hay algunas banderas que ayudan, pero las banderas no captarán todos los matices de su aplicación y las banderas no lo mantendrán vivo durante semanas como lo onSaveInstanceStatehará. Tienes que codificar el currículum perfecto dos semanas después. No será una tarea fácil para la aplicación compleja, pero estamos detrás de usted y estamos aquí para ayudarlo.

Buena suerte

danny117
fuente
Esto no se debe a problemas comunes de memoria o condiciones predeterminadas de "estado de pausa". He probado mi aplicación en muchos dispositivos (algunos con memoria grande y otros con poca memoria).
Graeme
Esta es la razón por la que guarda datos persistentes en onPause.He intentado que mi aplicación se reanude hasta dos semanas en el teléfono que uso todos los días y permítame decirle que después de dos semanas se llama al método onCreate y el sistema operativo pasa en el paquete que guardé en onSaveSessionState y yo utilizamos los datos del paquete para hacer que mi actividad aparezca exactamente como la dejé. Así que solo han pasado tres días desde mi respuesta, por lo que no hay forma de que puedas completar una prueba de dos semanas. Resumen: la aplicación se puede cerrar en cualquier momento que esté en segundo plano.
danny117
@ danny117 No creo que tenga una comprensión precisa del problema que Graeme está experimentando
starkej2
Entiendo el problema de Graeme. En algunos dispositivos, la reanudación de la aplicación llama a Crear. Suena exactamente como mi HTC EVO (Gingerbread) que mataría la aplicación solo para rotar la pantalla. Mire los documentos que dice para que la aplicación pueda restaurarse en onCreate developer.android.com/reference/android/app/…
danny117
@ danny117 eso es correcto, pero el problema que tiene no está relacionado con una sola actividad que se recrea cuando se reanuda la aplicación (lo que se espera), pero se está iniciando la actividad incorrecta
starkej2