android - samsung - La aplicación se reinicia en lugar de reanudarse
porque se apaga mi celular de repente (6)
Espero que alguien pueda ayudarme a descubrir, si no una solución, al menos una explicación para un comportamiento.
El problema:
En algunos dispositivos, al presionar el ícono del iniciador se reanuda la tarea actual; en otros, se inicia el intento de inicio inicial (reiniciando la aplicación de manera efectiva). ¿Por qué pasó esto?
El detalle:
Cuando presiona el "icono del iniciador", la aplicación comienza normalmente. Es decir, supongo que se inicia una intención con el nombre de su primera Activity
con la acción android.intent.action.MAIN
y 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, se reanuda la Activity
actualmente en ejecución en ese proceso ( NO la Activity
inicial). Se reanuda de la misma manera que si lo hubiera seleccionado desde "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á la
Activity
inicio inicial independientemente de lo que se esté ejecutando actualmente. Supongo que los iconos del iniciador siempre comienzan el intento de "LANZADOR".En la Samsung Tab 2, cuando presiona el ícono del iniciador, si acaba de instalar la aplicación, siempre iniciará la
Activity
inicial (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 al inicio del dispositivo, lo que permite que los íconos del iniciador reanuden correctamente las tareas en ejecución.
He leído muchas respuestas que suenan similares a mi problema, pero simplemente agregar android:alwaysRetainTaskState="true"
o usar launchMode="singleTop"
para la Activity
no es la respuesta.
Editar:
Después del lanzamiento más reciente de esta aplicación, encontramos que este comportamiento comenzó a ocurrir en todos los dispositivos después del primer reinicio. Lo cual me parece una locura, pero al revisar el proceso de reinicio, no puedo encontrar lo que está pasando mal.
Aha! (tldr; Vea las declaraciones en negrita en la parte inferior)
He encontrado el problema ... creo.
Entonces, comenzaré con una suposición. Cuando presiona el selector, inicia la Activity
predeterminada o, si una Task
iniciada por un inicio anterior está abierta, la lleva al frente. Dicho de otra manera: si en cualquier etapa de su navegación crea una nueva Task
y finish
la 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 Task
está en el mismo proceso y es tan válida como un candidato a currículum como el primero creado.
Mi problema, entonces, se solucionó eliminando estos indicadores 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_TASK
crea una nueva Task
, no me gustó que la suposición anterior estuviera en vigor. Lo consideré un culpable y lo quité 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 bienvenida estaba comenzando la actividad "principal" en mi aplicación usando la bandera de arriba. Después de todo, si "reiniciara" mi aplicación y la Activity
aún se estuviera ejecutando, preferiría preservar su información de estado.
Notará en la documentation que no menciona el inicio de una nueva Task
:
Si se establece, y la actividad que se está iniciando ya se está ejecutando en la tarea actual, en lugar de lanzar una nueva instancia de esa actividad, todas las demás actividades que se encuentren encima se cerrarán y esta intención se entregará al (ahora encendido arriba) actividad anterior como un nuevo propósito.
Por ejemplo, considere una tarea que consista en las actividades: A, B, C, D. Si D llama a startActivity () con un Intento que se resuelve en el componente de la actividad B, entonces C y D estarán terminados y B recibirá el Intento dado , dando como resultado que la pila sea ahora: A, B.
La instancia actualmente en ejecución de la actividad B en el ejemplo anterior recibirá el nuevo intento que está comenzando aquí en su método onNewIntent (), o se terminará y se reiniciará con el nuevo intento. Si ha declarado que su modo de lanzamiento es "múltiple" (el valor predeterminado) y no ha configurado FLAG_ACTIVITY_SINGLE_TOP en el mismo intento, entonces se terminará y se volverá a crear; para todos los demás modos de ejecución o si se establece FLAG_ACTIVITY_SINGLE_TOP, este Intent se entregará a la instancia actual onNewIntent ().
Este modo de lanzamiento 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 en ejecución y luego lo 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:
-
A
B
lanzado conFLAG_ACTIVITY_CLEAR_TOP
,A
finaliza. -
B
desea reiniciar un servicio, por lo que envía al usuario aA
que tiene la lógica de reinicio del servicio y la IU (Sin banderas). -
A
iniciaB
con FLAG_ACTIVITY_CLEAR_TOP,A
finaliza.
En esta etapa, el segundo indicador FLAG_ACTIVITY_CLEAR_TOP
está reiniciando B
que está en la pila de tareas. Estoy asumiendo que esto debe destruir la Task
y comenzar una nueva, 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
solo reanuda la tarea creada inicialmente -
FLAG_ACTIVITY_CLEAR_TOP
, si reinicia la únicaActivity
restante, también recreará una nuevaTask
El comportamiento que está experimentando se debe a un problema que existe en algunos iniciadores de Android desde la API 1. Puede encontrar detalles sobre el error y las posibles soluciones aquí: https://code.google.com/p/android/issues/detail?id=2373 .
Es un problema relativamente común en los dispositivos de Samsung y en otros fabricantes que usan un lanzador / protector personalizado. No he visto el problema en un programa de lanzamiento de Android.
Básicamente, la aplicación no se reinicia por completo, pero la actividad de inicio se inicia y se agrega 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. A continuación, debe llevarlo a la actividad que esperaba que se muestre cuando reanudó la aplicación.
La solución alternativa que decidí implementar para resolver este problema es verificar la categoría Intent.CATEGORY_LAUNCHER y la acción Intent.ACTION_MAIN en la intención que inicia la Actividad inicial. Si esos dos indicadores están presentes y la actividad no está en la raíz de la tarea (es decir, la aplicación ya se estaba ejecutando), entonces invoco finish () en la actividad inicial. Esa solución exacta puede no funcionar para usted, pero algo similar debería.
Esto es lo que hago en onCreate () de la actividad inicial / de inicio:
if (!isTaskRoot()
&& getIntent().hasCategory(Intent.CATEGORY_LAUNCHER)
&& getIntent().getAction() != null
&& getIntent().getAction().equals(Intent.ACTION_MAIN)) {
finish();
return;
}
Esta pregunta aún es relevante en 2016. Hoy, un comprobador de control de calidad informó que una aplicación mía se reinicia en lugar de reanudarse desde el iniciador de stock en Android M.
En realidad, el sistema estaba agregando la actividad lanzada a la task-stack actual, pero al usuario le pareció que se había reiniciado y habían perdido su trabajo. La secuencia fue:
- Descargar de Play Store (o apk de carga lateral)
- Inicie la aplicación desde el cuadro de diálogo de Play Store: aparece la actividad A [pila de tareas: A]
- Navegue a la actividad B [pila de tareas: A -> B]
- Presione el botón ''Inicio''
- Inicie la aplicación desde el cajón de la aplicación: ¡aparece la actividad A! [pila de tareas: A -> B -> A] (el usuario podría presionar el botón ''Atrás'' para ir a la actividad ''B'' desde aquí)
Nota: este problema no se manifiesta para los depuradores APK implementados a través de ADB, solo en los APK descargados de Play Store o de carga lateral. En los últimos casos, el intento de lanzamiento del paso 5 contenía el indicador Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
, pero no en los casos de depuración. El problema desaparece una vez que la aplicación ha sido iniciada en frío desde el iniciador. Mi sospecha es que la tarea está sembrada con una intención malformada (más exactamente, no estándar) que impide el comportamiento de inicio correcto hasta que la tarea se borre por completo.
Intenté varios modos de lanzamiento de actividad , pero esos ajustes se desviaron 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 Back Stack , en la parte inferior de la página en ''Iniciar una tarea'':
Un filtro de intención de este tipo hace que un ícono y etiqueta para la actividad se muestre en el iniciador de aplicaciones, brindando a los usuarios una forma de iniciar la actividad y regresar 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 del análisis de indicadores de intención para consultar si la actividad está en la raíz de la tarea directamente. Los indicadores de intención son difíciles de predecir y probar con todas las formas diferentes de abrir una actividad PRINCIPAL (Lanzamiento desde casa, inicio desde el botón ''subir'', lanzamiento desde Play Store, etc.)
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
puede no funcionar en todos los dispositivos, pero crea correctamente el comportamiento del botón de inicio cuando se presiona el botón Atrás, deteniendo la actividad en lugar de terminarla.
No tiene precio para tus usuarios. El currículum perfecto incluso después de semanas enteras en la lista de aplicaciones recientemente utilizadas.
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 y que no han iniciado una tarea es fácil de recuperar. El sistema operativo puede simplemente reiniciar la aplicación con el paquete original pasado a onCreate. Sin embargo, puede agregar al paquete original en onSaveInstanceState
para que cuando su aplicación sea reiniciada por el sistema operativo pueda restaurar el estado de la instancia y nadie sepa si la aplicación se reinició o reanudó. Tomemos, por ejemplo, el clásico programa de mapas. 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 mapas 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 usadas recientemente sino que también guarda el paquete original utilizado para iniciar la aplicación. Sin embargo, el programador ha codificado el método onSaveInstanceState
para que el paquete orignal ahora contenga todos los materiales y la información necesarios para construir la aplicación para que parezca que se reanudó.
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 desde 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 método onRestoreInstanceState
, pero me resulta más fácil restaurar la instancia en onCreate
.
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 recogerán todos los matices de tu aplicación y las banderas no te mantendrán con vida durante semanas como onSaveInstanceState
hará onSaveInstanceState
. Tienes que codificar el currículum perfecto de 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
Tuve el mismo problema en los dispositivos de Samsung. Después de buscar mucho, ninguna de estas respuestas funcionó para mí. Encontré que en el archivo AndroidManifest.xml , launchMode
se establece en singleInstance
( android:launchMode="singleInstance"
). Eliminar el atributo launchMode
solucionó mi problema.