Estoy tratando de escribir una aplicación que haga algo específico cuando se vuelve a poner en primer plano después de un tiempo. ¿Hay alguna manera de detectar cuándo una aplicación se envía al fondo o se pone en primer plano?
android
background
foreground
iHorse
fuente
fuente
Respuestas:
Los métodos
onPause()
yonResume()
se invocan cuando la aplicación se lleva al fondo y al primer plano nuevamente. Sin embargo, también se les llama cuando la aplicación se inicia por primera vez y antes de que se elimine. Puedes leer más en Actividad .No hay ningún enfoque directo para obtener el estado de la aplicación mientras está en segundo plano o en primer plano, pero incluso me he enfrentado a este problema y encontré la solución con
onWindowFocusChanged
yonStop
.Para obtener más detalles, consulte aquí Android: solución para detectar cuándo una aplicación de Android pasa a segundo plano y vuelve a primer plano sin getRunningTasks o getRunningAppProcesses .
fuente
2018: Android admite esto de forma nativa a través de componentes del ciclo de vida.
Actualización de marzo de 2018 : ahora hay una mejor solución. Ver ProcessLifecycleOwner . Deberá usar los nuevos componentes de arquitectura 1.1.0 (más recientes en este momento) pero es específicamente diseñado para hacerlo.
Hay una muestra simple proporcionada en esta respuesta, pero escribí una aplicación de muestra y una publicación de blog respecto.
Desde que escribí esto en 2014, surgieron diferentes soluciones. Algunos funcionaron, se pensó que algunos estaban trabajando , pero tenían fallas (¡incluida la mía!) Y nosotros, como comunidad (Android) aprendimos a vivir con las consecuencias y escribimos soluciones para los casos especiales.
Nunca asuma que un solo fragmento de código es la solución que está buscando, es poco probable; mejor aún, trate de entender qué hace y por qué lo hace.
La
MemoryBoss
clase nunca fue utilizada por mí como está escrita aquí, solo fue una pieza de pseudocódigo que funcionó.A menos que haya una razón válida para que no use los nuevos componentes de arquitectura (y hay algunos, especialmente si apunta a apis súper antiguas), continúe y utilícelos. Están lejos de ser perfectos, pero tampoco
ComponentCallbacks2
.ACTUALIZACIÓN / NOTAS (noviembre de 2015) : la gente ha estado haciendo dos comentarios, primero es que
>=
debe usarse en lugar de==
porque la documentación establece que no debe verificar los valores exactos . Esto está bien para la mayoría de los casos, pero tenga en cuenta que si solo le importa hacer algo cuando la aplicación pasó a segundo plano, tendrá que usar == y también combinarlo con otra solución (como las devoluciones de llamada de Activity Lifecycle), o usted Es posible que no obtenga el efecto deseado. El ejemplo (y esto me pasó a mí) es que si quieres bloquearsu aplicación con una pantalla de contraseña cuando pasa a segundo plano (como 1Password si está familiarizado con ella), puede bloquear accidentalmente su aplicación si se queda sin memoria y de repente la prueba>= TRIM_MEMORY
, porque Android activará unaLOW MEMORY
llamada y eso es más alto que el tuyo Así que tenga cuidado de cómo / qué prueba.Además, algunas personas han preguntado cómo detectar cuándo regresas.
La forma más simple que se me ocurre se explica a continuación, pero dado que algunas personas no están familiarizadas con esto, estoy agregando algunos pseudocódigos aquí. Suponiendo que tiene
YourApplication
y lasMemoryBoss
clases, en suclass BaseActivity extends Activity
(necesitará crear uno si no tiene uno).Recomiendo onStart porque los cuadros de diálogo pueden pausar una actividad, por lo que apuesto a que no desea que su aplicación piense "pasó a un segundo plano" si todo lo que hizo fue mostrar un cuadro de diálogo de pantalla completa, pero su kilometraje puede variar.
Y eso es todo. El código en el bloque if solo se ejecutará una vez , incluso si va a otra actividad, la nueva (que también
extends BaseActivity
) informaráwasInBackground
esfalse
para que no ejecute el código, hasta queonMemoryTrimmed
se llame y el indicador se establezca en verdadero nuevamente .Espero que ayude.
ACTUALIZACIÓN / NOTAS (abril de 2015) : antes de comenzar a copiar y pegar este código, tenga en cuenta que he encontrado un par de casos en los que puede no ser 100% confiable y debe combinarse con otros métodos para lograr los mejores resultados. En particular, hay dos instancias conocidas en las que
onTrimMemory
no se garantiza la ejecución de la devolución de llamada:Si su teléfono bloquea la pantalla mientras su aplicación está visible (digamos que su dispositivo se bloquea después de nn minutos), esta devolución de llamada no se llama (o no siempre) porque la pantalla de bloqueo está en la parte superior, pero su aplicación aún está "ejecutándose" aunque cubierta.
Si su dispositivo tiene relativamente poca memoria (y bajo estrés de memoria), el sistema operativo parece ignorar esta llamada y pasar directamente a niveles más críticos.
Ahora, dependiendo de lo importante que sea para usted saber cuándo su aplicación pasó a un segundo plano, es posible que necesite o no extender esta solución junto con realizar un seguimiento del ciclo de vida de la actividad y otras cosas.
Solo tenga en cuenta lo anterior y tenga un buen equipo de control de calidad;)
FIN DE ACTUALIZACIÓN
Puede ser tarde, pero hay un método confiable en Ice Cream Sandwich (API 14) y superior .
Resulta que cuando su aplicación ya no tiene una IU visible, se activa una devolución de llamada. La devolución de llamada, que puede implementar en una clase personalizada, se llama ComponentCallbacks2 (sí, con un dos). Esta devolución de llamada solo está disponible en API Nivel 14 (Ice Cream Sandwich) y superiores.
Básicamente recibes una llamada al método:
El nivel es 20 o más específicamente
He estado probando esto y siempre funciona, porque el nivel 20 es solo una "sugerencia" de que es posible que desee liberar algunos recursos ya que su aplicación ya no es visible.
Para citar los documentos oficiales:
Por supuesto, debe implementar esto para hacer lo que dice (purgar la memoria que no se ha utilizado en cierto tiempo, borrar algunas colecciones que no se han utilizado, etc.) Las posibilidades son infinitas (consulte los documentos oficiales para obtener más información). niveles críticos ).
Pero, lo interesante, es que el sistema operativo te dice: ¡HEY, tu aplicación pasó a un segundo plano!
Que es exactamente lo que querías saber en primer lugar.
¿Cómo determina cuándo regresó?
Bueno, eso es fácil, estoy seguro de que tienes una "BaseActivity" para que puedas usar tu onResume () para marcar el hecho de que has vuelto. Porque la única vez que dirá que no ha regresado es cuando realmente recibe una llamada al
onTrimMemory
método anterior .Funciona. No obtienes falsos positivos. Si se reanuda una actividad, estás de vuelta el 100% de las veces Si el usuario vuelve a la parte posterior, recibe otra
onTrimMemory()
llamada.Debe suscribir sus Actividades (o mejor aún, una clase personalizada).
La forma más fácil de garantizar que siempre reciba esto es crear una clase simple como esta:
Para usar esto, en la implementación de su aplicación ( tiene una, ¿DERECHO? ), Haga algo como:
Si crea una
Interface
se podría añadir unaelse
a eseif
e implementarComponentCallbacks
(sin la 2) usado en nada por debajo de la API de devolución de llamada 14. Que sólo tiene elonLowMemory()
método y no ser llamado cuando vas a un segundo plano , pero que se debe utilizar para la memoria de ajuste .Ahora inicie su aplicación y presione inicio. Su
onTrimMemory(final int level)
método debería llamarse (pista: agregar registro).El último paso es cancelar el registro de la devolución de llamada. Probablemente el mejor lugar es el
onTerminate()
método de su aplicación, pero ese método no se llama en un dispositivo real:Entonces, a menos que realmente tenga una situación en la que ya no quiera estar registrado, puede ignorarlo, ya que su proceso está muriendo a nivel del sistema operativo de todos modos.
Si decide cancelar el registro en algún momento (si, por ejemplo, proporciona un mecanismo de apagado para que su aplicación se limpie y muera), puede hacer lo siguiente:
Y eso es.
fuente
level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
que evita el problema en su actualización, punto 2. Con respecto al punto 1, no es una preocupación para mí, ya que la aplicación realmente no pasó a un segundo plano, por lo que se supone que funciona.Así es como me las arreglé para resolver esto. Funciona bajo la premisa de que el uso de una referencia de tiempo entre transiciones de actividad probablemente proporcionará evidencia adecuada de que una aplicación ha sido "en segundo plano" o no.
Primero, he usado una instancia de android.app.Application (llamémosla MyApplication) que tiene un Timer, un TimerTask, una constante para representar el número máximo de milisegundos que la transición de una actividad a otra podría llevar razonablemente (fui con un valor de 2s) y un valor booleano para indicar si la aplicación estaba "en segundo plano":
La aplicación también proporciona dos métodos para iniciar y detener el temporizador / tarea:
La última parte de esta solución es agregar una llamada a cada uno de estos métodos desde los eventos onResume () y onPause () de todas las actividades o, preferiblemente, en una Actividad base de la cual todas sus Actividades concretas heredan:
Entonces, en el caso de que el usuario simplemente navegue entre las actividades de su aplicación, el onPause () de la actividad de partida inicia el temporizador, pero casi de inmediato la nueva actividad que se ingresa cancela el temporizador antes de que pueda alcanzar el tiempo máximo de transición. Y también lo eraInBackground sería falso .
Por otro lado, cuando una Actividad aparece en primer plano desde el Iniciador, el dispositivo se activa, finaliza la llamada telefónica, etc., es más que probable que la tarea del temporizador se haya ejecutado antes de este evento y, por lo tanto, wasInBackground se haya establecido en verdadero .
fuente
Editar: los nuevos componentes de arquitectura trajeron algo prometedor: ProcessLifecycleOwner , vea la respuesta de @ vokilam
La solución real de acuerdo con una charla de E / S de Google :
Si. Sé que es difícil creer que esta solución simple funcione ya que tenemos tantas soluciones extrañas aquí.
Pero hay esperanza.
fuente
ProcessLifecycleOwner
Parece ser una solución prometedora también.Una implementación puede ser tan simple como
Según el código fuente, el valor actual de retraso es
700ms
.También usar esta función requiere
dependencies
:fuente
implementation "android.arch.lifecycle:extensions:1.0.0"
yannotationProcessor "android.arch.lifecycle:compiler:1.0.0"
del repositorio de Google (es decirgoogle()
)Basado en la respuesta de Martín Marconcinis (¡gracias!) Finalmente encontré una solución confiable (y muy simple).
Luego agregue esto a su onCreate () de su clase de aplicación
fuente
Usamos este método. Parece demasiado simple para trabajar, pero se probó bien en nuestra aplicación y, de hecho, funciona sorprendentemente bien en todos los casos, incluyendo ir a la pantalla de inicio con el botón "inicio", con el botón "regresar" o después del bloqueo de pantalla. Darle una oportunidad.
La idea es que, en primer plano, Android siempre comienza una nueva actividad justo antes de detener la anterior. Eso no está garantizado, pero así es como funciona. Por cierto, Flurry parece usar la misma lógica (solo una suposición, no lo comprobé, pero se engancha en los mismos eventos).
Editar: según los comentarios, también nos mudamos a onStart () en versiones posteriores del código. Además, estoy agregando super llamadas, que faltaban en mi publicación inicial, porque esto era más un concepto que un código de trabajo.
fuente
onStop is called when the activity is no longer visible to the user
.Si su aplicación consta de múltiples actividades y / o actividades apiladas como un widget de barra de pestañas, anular onPause () y onResume () no funcionará. Es decir, al comenzar una nueva actividad, las actividades actuales se pausarán antes de que se cree la nueva. Lo mismo se aplica al terminar (usando el botón "atrás") una actividad.
He encontrado dos métodos que parecen funcionar según lo deseado.
El primero requiere el permiso GET_TASKS y consiste en un método simple que verifica si la actividad principal en ejecución en el dispositivo pertenece a la aplicación, comparando los nombres de los paquetes:
Este método se encontró en el marco Droid-Fu (ahora llamado Ignition).
El segundo método que he implementado no requiere el permiso GET_TASKS, lo cual es bueno. En cambio, es un poco más complicado de implementar.
En su clase MainApplication tiene una variable que rastrea el número de actividades en ejecución en su aplicación. En onResume () para cada actividad aumentas la variable y en onPause () la disminuyes.
Cuando el número de actividades en ejecución llega a 0, la aplicación se pone en segundo plano SI se cumplen las siguientes condiciones:
Cuando puede detectar que la aplicación ha renunciado a un segundo plano, también es fácil detectar cuándo se vuelve a poner en primer plano.
fuente
getRunnintTasks()
Crea una clase que se extienda
Application
. Luego, en ella podemos utilizar su método de reemplazo,onTrimMemory()
.Para detectar si la aplicación pasó a un segundo plano, utilizaremos:
fuente
FragmentActivity
usted también puede querer agregarlevel == ComponentCallbacks2.TRIM_MEMORY_COMPLETE
también.Considere usar onUserLeaveHint. Esto solo se llamará cuando su aplicación pase a un segundo plano. onPause tendrá casos de esquina para manejar, ya que se puede llamar por otros motivos; por ejemplo, si el usuario abre otra actividad en su aplicación, como su página de configuración, se llamará al método onPause de su actividad principal aunque todavía esté en su aplicación; el seguimiento de lo que está sucediendo generará errores cuando en su lugar pueda simplemente usar la devolución de llamada onUserLeaveHint que hace lo que está pidiendo.
Cuando se llama a UserLeaveHint, puede establecer un indicador booleano inBackground en true. Cuando se llama a onResume, solo asuma que regresó al primer plano si la bandera inBackground está activada. Esto se debe a que onResume también se llamará a su actividad principal si el usuario solo estaba en su menú de configuración y nunca abandonó la aplicación.
Recuerde que si el usuario presiona el botón de inicio mientras está en su pantalla de configuración, onUserLeaveHint se llamará en su actividad de configuración, y cuando regrese a OnResume se llamará en su actividad de configuración. Si solo tiene este código de detección en su actividad principal, se perderá este caso de uso. Para tener este código en todas sus actividades sin duplicar el código, tenga una clase de actividad abstracta que extienda la Actividad y coloque su código común. Entonces, cada actividad que tenga puede extender esta actividad abstracta.
Por ejemplo:
fuente
ActivityLifecycleCallbacks puede ser de interés, pero no está bien documentado.
Sin embargo, si llama a registerActivityLifecycleCallbacks (), debería poder obtener devoluciones de llamada para cuando las Actividades se crean, destruyen, etc. Puede llamar a getComponentName () para la Actividad.
fuente
El paquete android.arch.lifecycle proporciona clases e interfaces que le permiten crear componentes que reconocen el ciclo de vida.
Su aplicación debe implementar la interfaz LifecycleObserver:
Para hacer eso, debe agregar esta dependencia a su archivo build.gradle:
Según lo recomendado por Google, debe minimizar el código ejecutado en los métodos de actividades del ciclo de vida:
Puede leer más aquí: https://developer.android.com/topic/libraries/architecture/lifecycle
fuente
En su aplicación, agregue la devolución de llamada y verifique la actividad raíz de esta manera:
fuente
He creado un proyecto en Github app-foreground-background-listen
Cree una BaseActivity para toda la actividad en su aplicación.
Ahora use esta BaseActivity como una superclase de toda su Actividad, como MainActivity extiende BaseActivity y onAppStart se llamará cuando inicie su aplicación y onAppPause () se llamará cuando la aplicación pase al fondo desde cualquier pantalla.
fuente
Esto es bastante fácil con ProcessLifecycleOwner
Agregar estas dependencias
En Kotlin :
Luego, en su actividad base:
Vea mi artículo sobre este tema: https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48
fuente
Puede usar ProcessLifecycleOwner adjuntando un observador del ciclo de vida.
entonces en el
onCreate()
de su clase de Aplicación, llama a esto:con esto podrás capturar los eventos de
ON_PAUSE
yON_STOP
de tu aplicación que suceden cuando pasa a segundo plano.fuente
No hay métodos directos del ciclo de vida que le indiquen cuándo toda la aplicación pasa a segundo plano.
He hecho esto de manera simple. Siga las instrucciones a continuación para detectar la fase de fondo / primer plano de la aplicación.
Con una pequeña solución, es posible. Aquí, ActivityLifecycleCallbacks viene al rescate. Déjame caminar paso a paso.
Primero, cree una clase que extienda android.app.Application e implemente la interfaz ActivityLifecycleCallbacks . En Application.onCreate (), registre la devolución de llamada.
Registrar la clase “App” en el Manifiesto de la siguiente manera,
<application android:name=".App"
.Habrá al menos una Actividad en el estado iniciado cuando la aplicación esté en primer plano y no habrá Actividad en el estado iniciado cuando la aplicación esté en segundo plano.
Declare 2 variables como a continuación en la clase "Aplicación".
activityReferences
mantendrá el recuento de la cantidad de actividades en el estado iniciado .isActivityChangingConfigurations
es una bandera para indicar si la Actividad actual está pasando por un cambio de configuración como un interruptor de orientación.Con el siguiente código, puede detectar si la aplicación aparece en primer plano.
Así es como detectar si la aplicación pasa a segundo plano.
Cómo funciona:
Este es un pequeño truco hecho con la forma en que se llaman los métodos del Ciclo de vida en secuencia. Déjame pasar por un escenario.
Suponga que el usuario inicia la aplicación y se inicia la actividad de inicio A. Las llamadas del ciclo de vida serán,
Ahora la actividad A comienza la actividad B.
Luego, el usuario navega hacia atrás desde la Actividad B,
Luego, el usuario presiona el botón Inicio,
En caso de que, si el usuario presiona el botón de Inicio de la Actividad B en lugar del botón Atrás, seguirá siendo el mismo y las referencias de actividad serán
0
. Por lo tanto, podemos detectar como la aplicación entrando en segundo plano.Entonces, ¿cuál es el papel de
isActivityChangingConfigurations
? En el escenario anterior, suponga que la Actividad B cambia la orientación. La secuencia de devolución de llamada será,Es por eso que tenemos una verificación adicional
isActivityChangingConfigurations
para evitar el escenario cuando la Actividad está pasando por los cambios de Configuración.fuente
Encontré un buen método para detectar aplicaciones, ya sea que ingrese en primer plano o en segundo plano. Aquí está mi código . Espero que esto te ayude.
}
fuente
Puedes usar:
Para diferir entre nuevos comienzos y reinicios.
fuente
Edición 2: lo que he escrito a continuación en realidad no funcionará. Google ha rechazado una aplicación que incluye una llamada a ActivityManager.getRunningTasks (). De la documentación , es evidente que esta API es solo para fines de depuración y desarrollo. Actualizaré esta publicación tan pronto como tenga tiempo para actualizar el proyecto de GitHub a continuación con un nuevo esquema que usa temporizadores y es casi tan bueno.
Edición 1: escribí una publicación de blog y creé un repositorio simple de GitHub para que esto sea realmente fácil.
La respuesta aceptada y mejor calificada no son realmente el mejor enfoque. La implementación de la respuesta mejor calificada de isApplicationBroughtToBackground () no maneja la situación en la que la Actividad principal de la Aplicación está cediendo a una Actividad que está definida en la misma Aplicación, pero tiene un paquete Java diferente. Se me ocurrió una manera de hacer esto que funcionará en ese caso.
Llame a esto en onPause (), y le dirá si su aplicación está en segundo plano porque otra aplicación se ha iniciado o si el usuario ha presionado el botón de inicio.
fuente
Respuesta correcta aquí
Cree una clase con el nombre MyApp como se muestra a continuación:
Luego, donde quiera (mejor primera actividad iniciada en la aplicación), agregue el siguiente código:
¡Hecho! Ahora, cuando la aplicación está en segundo plano, obtenemos un registro
status : we are out
y cuando entramos en la aplicación, obtenemos un registrostatus : we are out
fuente
Mi solución se inspiró en la respuesta de @ d60402 y también se basa en una ventana de tiempo, pero no usando
Timer
:donde el
SingletonApplication
es una extensión deApplication
clase:fuente
Estaba usando esto con Google Analytics EasyTracker, y funcionó. Podría extenderse para hacer lo que busca utilizando un entero simple.
fuente
Sé que es un poco tarde, pero creo que todas estas respuestas tienen algunos problemas mientras lo hice a continuación y eso funciona perfecto.
cree una devolución de llamada del ciclo de vida de la actividad como esta:
y simplemente regístralo en tu clase de aplicación como a continuación:
fuente
Esta parece ser una de las preguntas más complicadas en Android ya que (al momento de escribir esto) Android no tiene equivalentes
applicationDidEnterBackground()
oapplicationWillEnterForeground()
devoluciones de llamada de iOS . Usé una biblioteca de AppState que fue creada por @jenzz .Resultó que esto es exactamente lo que necesitaba, especialmente porque mi aplicación tenía múltiples actividades, por lo que simplemente verificar
onStart()
o realizaronStop()
una actividad no iba a ser suficiente.Primero agregué estas dependencias a gradle:
Luego fue una simple cuestión de agregar estas líneas a un lugar apropiado en su código:
Dependiendo de cómo se suscriba al observable, es posible que deba cancelar su suscripción para evitar pérdidas de memoria. Nuevamente más información en la página de github .
fuente
Esta es la versión modificada de la respuesta de @ d60402: https://stackoverflow.com/a/15573121/4747587
Haz todo lo mencionado allí. Pero en lugar de tener un
Base Activity
y hacer eso como padre para cada actividad y anular elonResume()
yonPause
, haga lo siguiente:En su clase de aplicación, agregue la línea:
registerActivityLifecycleCallbacks (devolución de llamada Application.ActivityLifecycleCallbacks);
Esto
callback
tiene todos los métodos de ciclo de vida de la actividad y ahora puede anularonActivityResumed()
yonActivityPaused()
.Echa un vistazo a este Gist: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b
fuente
Esto se puede conseguir fácilmente con la ayuda de
ActivityLifecycleCallbacks
yComponentCallbacks2
algo parecido a continuación.Cree una clase
AppLifeCycleHandler
implementando por encima de dichas interfaces.En su clase, que amplía el
Application
implementoAppLifeCycleCallback
para obtener las devoluciones de llamada cuando la aplicación cambia entre primer plano y fondo. Algo como abajo.Espero que esto ayude.
EDITAR Como alternativa, ahora puede utilizar el componente de arquitectura consciente del ciclo de vida.
fuente
Como no encontré ningún enfoque, que también maneja la rotación sin verificar las marcas de tiempo, pensé que también compartiría cómo lo hacemos ahora en nuestra aplicación. La única adición a esta respuesta https://stackoverflow.com/a/42679191/5119746 es que también tenemos en cuenta la orientación.
Luego, para las devoluciones de llamada, primero tenemos el currículum:
Y enActivityStopped:
Y luego, aquí viene la adición: Verificar cambios de orientación:
Eso es. Espero que esto ayude a alguien :)
fuente
Podemos ampliar esta solución usando
LiveData
:Ahora podemos suscribirnos a este LiveData y capturar los eventos necesarios. Por ejemplo:
fuente
Estas respuestas no parecen ser correctas. Estos métodos también se llaman cuando comienza y termina otra actividad. Lo que puede hacer es mantener una bandera global (sí, los globales son malos :) y establecer esto en verdadero cada vez que comience una nueva actividad. Póngalo en falso en onCreate de cada actividad. Luego, en onPause, marca esta bandera. Si es falsa, su aplicación está pasando a un segundo plano o está siendo eliminada.
fuente