Comprobar si una aplicación de Android se ejecuta en segundo plano

329

Por antecedentes, ¿quiero decir que ninguna de las actividades de la aplicación es visible actualmente para el usuario?

cppdev
fuente
77
Estoy confundido aquí ... ¿por qué Android no puede proporcionar una anulación simple en la clase de aplicación para esto? ¿Es demasiado difícil saber esto a nivel de plataforma? @Override protegido vacío enApplicationSentToBackground () {}
Chuck D
2
@ChuckD: eso tendría sentido, que es lo que el SDK de Android parece evitar hacer a veces. : /
Mark
1
iOS tiene esto en picas, no estoy seguro de por qué Google lo hace tan difícil. Es una necesidad tan obvia.
Jerry Destremps

Respuestas:

388

Hay pocas formas de detectar si su aplicación se ejecuta en segundo plano, pero solo una de ellas es completamente confiable:

  1. La solución correcta (créditos van a Dan , CommonsWare y NeTeInStEiN )
    visibilidad de la pista de su aplicación por sí mismo utilizando Activity.onPause, Activity.onResumemétodos. Almacenar el estado de "visibilidad" en alguna otra clase. Las buenas opciones son su propia implementación de la Applicationo una Service(también hay algunas variaciones de esta solución si desea verificar la visibilidad de la actividad desde el servicio).
     
    Ejemplo
    Implementar Applicationclase personalizada (tenga en cuenta el isActivityVisible()método estático):

    public class MyApplication extends Application {
    
      public static boolean isActivityVisible() {
        return activityVisible;
      }  
    
      public static void activityResumed() {
        activityVisible = true;
      }
    
      public static void activityPaused() {
        activityVisible = false;
      }
    
      private static boolean activityVisible;
    }

    Registre su clase de aplicación en AndroidManifest.xml:

    <application
        android:name="your.app.package.MyApplication"
        android:icon="@drawable/icon"
        android:label="@string/app_name" >

    Agregue onPausey onResumea cada uno Activityen el proyecto (puede crear un antepasado común para sus Actividades si lo desea, pero si su actividad ya se ha extendido desde MapActivity/ ListActivityetc. aún debe escribir lo siguiente a mano):

    @Override
    protected void onResume() {
      super.onResume();
      MyApplication.activityResumed();
    }
    
    @Override
    protected void onPause() {
      super.onPause();
      MyApplication.activityPaused();
    }

     
    Update
    ActivityLifecycleCallbacks se agregaron en el nivel 14 de API (Android 4.0). Puede usarlos para rastrear si una actividad de su aplicación está actualmente visible para el usuario. Verifique la respuesta de Cornstalks a continuación para obtener más detalles.

  2. El incorrecto
    solía sugerir la siguiente solución:

    Puede detectar la aplicación actual en primer plano / fondo con la ActivityManager.getRunningAppProcesses()que devuelve una lista de RunningAppProcessInforegistros. Para determinar si su aplicación está en el RunningAppProcessInfo.importancecampo de verificación de primer plano para la igualdad RunningAppProcessInfo.IMPORTANCE_FOREGROUNDmientrasRunningAppProcessInfo.processName es igual al nombre del paquete de su aplicación.

    Además, si llama ActivityManager.getRunningAppProcesses()desde el subproceso de interfaz de usuario de la aplicación, le devolverá importancia IMPORTANCE_FOREGROUNDpara su tarea, sin importar si realmente está en primer plano o no. Llámelo en el hilo de fondo (por ejemplo, vía AsyncTask) y devolverá los resultados correctos.

    Si bien esta solución puede funcionar (y de hecho funciona la mayor parte del tiempo), recomiendo abstenerse de usarla. Y he aquí por qué. Como escribió Dianne Hackborn :

    Estas API no están ahí para que las aplicaciones basen su flujo de interfaz de usuario, sino para hacer cosas como mostrarle al usuario las aplicaciones en ejecución, o un administrador de tareas, o tal.

    Sí, hay una lista guardada en la memoria para estas cosas. Sin embargo, está desactivado en otro proceso, administrado por subprocesos que se ejecutan por separado del suyo, y no es algo con lo que pueda contar (a) ver a tiempo para tomar la decisión correcta o (b) tener una imagen coherente para cuando regrese. Además, la decisión acerca de cuál es la "próxima" actividad a la que se debe ir siempre se realiza en el punto donde se realizará el cambio, y no es hasta ese punto exacto (donde el estado de la actividad se bloquea brevemente para hacer el cambio) De hecho, sepa con certeza cuál será la próxima cosa.

    Y no se garantiza que la implementación y el comportamiento global aquí sigan siendo los mismos en el futuro.

    Desearía haber leído esto antes de publicar una respuesta en el SO, pero espero que no sea demasiado tarde para admitir mi error.

  3. Otra solución incorrecta que la biblioteca
    Droid-Fu mencionó en una de las respuestas utiliza ActivityManager.getRunningTaskspara su isApplicationBroughtToBackgroundmétodo. Vea el comentario de Dianne arriba y tampoco use ese método.

Ídolo
fuente
44
Para saber si presionó el botón de inicio o alguna otra aplicación se ha centrado: 1) implemente la buena solución . 2) En OnStopsolicitud de isActivityVisible.
Brais Gabin
28
Lamentablemente, su solución "correcta" no funciona para mí. Considere que recorre las actividades dentro de su aplicación. Lo que sucede entonces es que su bandera 'inForeground' es así: Verdadero, Falso (Entre la primera actividad en pausa y la segunda actividad en Reanudar), luego Verdadero nuevamente, etc. Luego necesitaría una histéresis de algún tipo.
Radu
14
Esta solución no funciona si no puede controlar directamente todas las actividades. Por ejemplo, si tiene una Actividad de un SDK de un tercero o incluso inicia una intención ACTION_VIEW.
user123321
66
Android es un desastre tan loco. ¿Nadie pensó que alguien podría querer persistir los datos de nivel de aplicación? Dame un respiro
8
Parece que la respuesta real a esta pregunta es "No puede verificarlo correctamente". La llamada solución 'correcta' es, en el mejor de los casos, una solución alternativa, también lo es usar ActivityLifecycleCallbacks. Aún debe considerar cambiar entre actividades que se registrarían como "no en primer plano". Me sorprende que no puedas comprobar una cosa tan simple como esa ...
Serine
263

NO USE ESTA RESPUESTA

La respuesta del usuario1269737 es la forma correcta (aprobada por Google / Android) de hacer esto . Ve a leer su respuesta y dales un +1.

Dejaré mi respuesta original aquí por el bien de la posteridad. Este fue el mejor disponible en 2012, pero ahora Android tiene el soporte adecuado para esto.

Respuesta original

La clave está usando ActivityLifecycleCallbacks(tenga en cuenta que esto requiere el nivel 14 de la API de Android (Android 4.0)). Simplemente verifique si el número de actividades detenidas es igual al número de actividades iniciadas. Si son iguales, su aplicación está en segundo plano. Si hay más actividades iniciadas, su aplicación aún está visible. Si hay más actividades reanudadas que pausadas, su aplicación no solo es visible, sino que también está en primer plano. Hay 3 estados principales en los que su actividad puede estar, entonces: visible y en primer plano, visible pero no en primer plano, y no visible y no en primer plano (es decir, en segundo plano).

Lo realmente bueno de este método es que no tiene los problemas asincrónicos getRunningTasks(), pero tampoco tiene que modificar cada uno Activityen su aplicación para configurar / desarmar algo en onResumed()/ onPaused(). Son solo unas pocas líneas de código que son independientes y funcionan en toda la aplicación. Además, tampoco se requieren permisos originales.

MyLifecycleHandler.java:

public class MyLifecycleHandler implements ActivityLifecycleCallbacks {
    // I use four separate variables here. You can, of course, just use two and
    // increment/decrement them instead of using four and incrementing them all.
    private int resumed;
    private int paused;
    private int started;
    private int stopped;

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {
        ++resumed;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        ++paused;
        android.util.Log.w("test", "application is in foreground: " + (resumed > paused));
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    }

    @Override
    public void onActivityStarted(Activity activity) {
        ++started;
    }

    @Override
    public void onActivityStopped(Activity activity) {
        ++stopped;
        android.util.Log.w("test", "application is visible: " + (started > stopped));
    }

    // If you want a static function you can use to check if your application is
    // foreground/background, you can use the following:
    /*
    // Replace the four variables above with these four
    private static int resumed;
    private static int paused;
    private static int started;
    private static int stopped;

    // And these two public static functions
    public static boolean isApplicationVisible() {
        return started > stopped;
    }

    public static boolean isApplicationInForeground() {
        return resumed > paused;
    }
    */
}

MyApplication.java:

// Don't forget to add it to your manifest by doing
// <application android:name="your.package.MyApplication" ...
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        // Simply add the handler, and that's it! No need to add any code
        // to every activity. Everything is contained in MyLifecycleHandler
        // with just a few lines of code. Now *that's* nice.
        registerActivityLifecycleCallbacks(new MyLifecycleHandler());
    }
}

@Mewzer ha hecho algunas buenas preguntas sobre este método a las que me gustaría responder en esta respuesta para todos:

onStop()no se llama en situaciones de poca memoria; ¿Es eso un problema aquí?

No. Los documentos para onStop()decir:

Tenga en cuenta que este método nunca se puede llamar, en situaciones de poca memoria donde el sistema no tiene suficiente memoria para mantener el proceso de su actividad ejecutándose después de que se llama a su método onPause ().

La clave aquí es "mantener el proceso de su actividad ejecutándose ..." Si alguna vez se llega a esta situación de poca memoria, su proceso se anula (no solo su actividad). Esto significa que este método de verificación de fondo sigue siendo válido porque a) no puede verificar el fondo de todos modos si su proceso se anula, yb) si su proceso comienza nuevamente (porque se crea una nueva actividad), el miembro las variables (ya sea estáticas o no) para MyLifecycleHandlerse restablecerán a 0.

¿Funciona esto para cambios de configuración?

Por defecto, no. Debe establecer explícitamente configChanges=orientation|screensize( |con cualquier otra cosa que desee) en su archivo de manifiesto y manejar los cambios de configuración, o su actividad será destruida y recreada. Si no se establece esto, los métodos de su actividad serán llamados en el orden siguiente: onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume. Como puede ver, no hay superposición (normalmente, dos actividades se superponen muy brevemente al cambiar entre las dos, que es cómo funciona este método de detección de fondo). Para evitar esto, debe configurarlo configChangespara que su actividad no se destruya. Afortunadamente, tuve que configurarconfigChangesYa en todos mis proyectos porque no era deseable que toda mi actividad se destruyera en la pantalla, gire / cambie el tamaño, por lo que nunca he encontrado que esto sea problemático. (¡Gracias a dpimka por refrescar mi memoria en esto y corregirme!)

Una nota:

Cuando dije "antecedentes" aquí en esta respuesta, quise decir "su aplicación ya no es visible". Las actividades de Android pueden ser visibles pero no estar en primer plano (por ejemplo, si hay una superposición de notificaciones transparente). Es por eso que actualicé esta respuesta para reflejar eso.

Es importante saber que Android tiene un momento de limbo extraño al cambiar actividades donde nada está en primer plano . Por esta razón, si verifica si su aplicación está en primer plano al cambiar entre actividades (en la misma aplicación), se le informará que no está en primer plano (aunque su aplicación sigue siendo la aplicación activa y está visible )

Puede comprobar si su aplicación está en primer plano en su Activity's onPause()método después super.onPause() . Solo recuerda el extraño estado del limbo del que acabo de hablar.

Puede comprobar si su aplicación es visible (es decir, si no es en el fondo) en su Activity's onStop()método después super.onStop() .

Tallos de maiz
fuente
1
Esto parece interesante, pero ¿qué sucede en situaciones de poca memoria? No se garantiza que se llamará a onStop (). ¿Podríamos entrar en la situación en la que no se llama a onStop () y el contador detenido no se incrementa, esto significa que la verificación de fondo ya no es confiable? ¿O esto nunca sucedería?
Mewzer
1
Además, ¿esto ignorará los cambios de configuración? ¿O la aplicación se considerará en segundo plano si una actividad se vuelve a crear como resultado de un cambio de configuración (por ejemplo, un cambio de orientación)? Lo siento, por las preguntas, pero creo que estás en algo y estoy interesado en saber si funciona en estos casos extremos.
Mewzer
1
@Mewzer: iba a responder como un comentario, pero me tomará un poco escribir para obtener estas respuestas, así que vuelva a consultar en unos minutos y editaré mi respuesta.
Tallo de maíz
1
@Mewzer: Deberías encontrar tus respuestas ahora. ¡Avíseme si hay alguna otra pregunta!
Tallo de maíz
2
@Mewzer: Acabo de agregar una nota en la que podría estar interesado. Específicamente, verifique los antecedentes onStop()después super.onStop(). No verifique el fondo en onPause().
Cornstalks
187

SOLUCIÓN DE GOOGLE : no es un truco, como las soluciones anteriores. Utilice ProcessLifecycleOwner

Kotlin:

class ArchLifecycleApp : Application(), LifecycleObserver {

    override fun onCreate() {
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onAppBackgrounded() {
        //App in background
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onAppForegrounded() {
        // App in foreground
    }

}


Java:

public class ArchLifecycleApp extends Application implements LifecycleObserver {

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onAppBackgrounded() {
        //App in background
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppForegrounded() {
        // App in foreground
    }
}

en app.gradle

dependencies {
    ...
    implementation "android.arch.lifecycle:extensions:1.1.0"

    //New Android X dependency is this - 
    implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"

}

allprojects {
    repositories {
        ...
        google()
        jcenter()
        maven { url 'https://maven.google.com' }
    }
}

Puede leer más sobre los componentes de arquitectura relacionados con el ciclo de vida aquí: https://developer.android.com/topic/libraries/architecture/lifecycle

usuario1269737
fuente
10
¡Esta definitivamente debería ser la respuesta correcta! Funcionó de
maravilla
2
Esto funciona perfectamente, también modifiqué un poco para poder acceder al estado de primer plano / fondo más fácilmente fuera de esta clase: companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }entonces puedes obtener el estado de primer plano conArchLifecycleApp.isForeground()
Jose Jet
2
Oh hombre, esto es mucho mejor que mi vieja respuesta. Ten un +1 de mi parte. Actualicé mi respuesta para señalar a las personas la suya.
Cornstalks
2
Aunque esta es una respuesta correcta, no es necesario implementar devoluciones de llamada, solo puede consultar ProcessLifecycleOwner cuando lo desee. Compruebe stackoverflow.com/a/52678290/6600000
Keivan Esbati el
2
Como dice el documento The LifecycleOwner for the whole application process. Note that if your application has multiple processes, this provider does not know about other processes. , esto no funciona para las multiple processesaplicaciones, ¿hay alguna API que podamos lograr con elegancia?
acntwww
23

A partir de la versión 26 de la biblioteca de soporte, puede usar ProcessLifecycleOwner , simplemente agréguelo a su dependencia como se describe aquí , por ejemplo:

dependencies {
    def lifecycle_version = "1.1.1"

    // ViewModel and LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    // alternatively - Lifecycles only (no ViewModel or LiveData).
    //     Support library depends on this lightweight import
    implementation "android.arch.lifecycle:runtime:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}

Y luego solo consulta ProcessLifecycleOwnercuando quieras el estado de la aplicación, ejemplos:

//Check if app is in background
ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED;

//Check if app is in foreground
ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
Keivan Esbati
fuente
2
Gracias, esta es la forma mejor y más fácil que es adecuada en cualquier parte de tu código, especialmente cuando usas fcm.
Mihae Kheel
Si la aplicación se cierra por completo, ¿cuál sería el primer método?
Evgeniy Mishustin
@EvgeniyMishustin que depende del estado actual de la aplicación, pero por lo general verías CREADO y DESTRUIDO y después de eso, no recibirías ningún evento nuevo.
Keivan Esbati
Entonces, ¿dónde está cualquier declaración "IF" para ver si la aplicación IF está en segundo plano (primer plano)
Ekashking
@ekashking acaba de poner la declaración completa en la cláusula if. Por ejemplo: if (ProcessLifecycleOwner.get (). GetLifecycle (). GetCurrentState (). IsAtLeast (Lifecycle.State.STARTED)) => La aplicación está en primer plano
Keivan Esbati
20

Desde Android API 16 hay una manera simple de verificar si la aplicación está en primer plano. Puede que no sea infalible, pero ningún método en Android es infalible. Este método es lo suficientemente bueno como para usarlo cuando su servicio recibe actualizaciones del servidor y tiene que decidir si mostrar notificación o no (porque si la IU está en primer plano, el usuario notará la actualización sin notificación).

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
Juozas Kontvainis
fuente
¿Debería estar este código dentro de la clase de servicio u otra clase, por ejemplo, la clase de aplicación? Muchas gracias.
Woppi el
... donde quiera que lo use, ya que la línea final es solo un booleano con el que comprobaría.
AO_
Esta es la misma metodología que AWS Android SDK para notificaciones push.
spakmad
Tenga en cuenta que "la definición de fondo para fines de limitaciones de servicio es distinta de la definición utilizada por la administración de memoria; una aplicación podría estar en segundo plano en lo que respecta a la administración de memoria, pero en primer plano en lo que respecta a su capacidad para iniciar servicios). " developer.android.com/about/versions/oreo/background.html (
ARLabs
Gracias, esto funcionó! Pude usar este código JobServicepara detectar que el servicio se está ejecutando en segundo plano.
Michael Osofsky
17

La respuesta de Idolon es propensa a errores y es mucho más complicada, aunque en este caso, verifique que la aplicación de Android esté en primer plano o no. y aquí Determinar la aplicación actual en primer plano a partir de una tarea o servicio en segundo plano

Hay un enfoque mucho más simple:

En una BaseActivity que todas las actividades se extienden:

protected static boolean isVisible = false;

 @Override
 public void onResume()
 {
     super.onResume();
     setVisible(true);
 }


 @Override
 public void onPause()
 {
     super.onPause();
     setVisible(false);
 }

Siempre que necesite verificar si alguna de las actividades de su aplicación está en primer plano, simplemente verifique isVisible() ;

Para comprender este enfoque, consulte esta respuesta del ciclo de vida de la actividad en paralelo : Ciclo de vida de la actividad en paralelo

Neteinstein
fuente
3
Idolon's answer is error prone- desafortunadamente tengo que estar de acuerdo contigo. Según el comentario de Dianne Hackborn en los Grupos de Google, actualicé mi respuesta. Compruébalo por favor para los detalles.
Idolon
2
Esta tampoco es una solución infalible. Un escenario es si el usuario tira hacia abajo el panel de notificaciones, ni el onPause, onStopni el onResumeevento se llama. Entonces, ¿qué haces si ninguno de estos eventos se dispara?
Esto me llevó a esta pregunta: stackoverflow.com/questions/33657102/…
Ruchir Baronia
Lamentablemente, pero este código funciona mal cuando se inicia una actividad cuando la pantalla está apagada. En este caso, onResume y onPause se llaman making isVisible = false.
CoolMind
@CoolMind ¿Puede explicar cuál es el caso de uso en el que iniciaría una actividad en segundo plano?
Neteinstein
11

Probé la solución recomendada que usa Application.ActivityLifecycleCallbacks y muchos otros, pero no funcionaron como se esperaba. Gracias a Sarge , se me ocurrió una solución bastante fácil y directa que describo a continuación.

La clave de la solución es el hecho de comprender que si tenemos ActivityA y ActivityB, y llamamos a ActivityB desde ActivityA (y no a call ActivityA.finish), onStart()se llamará a ActivityB antes que ActivityA onStop().

Esa es también la principal diferencia entre onStop()y onPause()que ninguno mencionó en los artículos que leí.

Entonces, según el comportamiento del ciclo de vida de esta actividad, simplemente puede contar cuántas veces lo hizo onStart()y onPause()recibió llamadas en su programa. Tenga en cuenta que para cada uno Activity de sus programas, debe anularonStart() yonStop() , para aumentar / disminuir la variable estática utilizada para contar. A continuación se muestra el código que implementa esta lógica. Tenga en cuenta que estoy usando una clase que se extiende Application, así que no se olvide de declarar en la Manifest.xmletiqueta de la aplicación: android:name=".Utilities"aunque también se puede implementar usando una clase personalizada simple.

public class Utilities extends Application
{
    private static int stateCounter;

    public void onCreate()
    {
        super.onCreate();
        stateCounter = 0;
    }

    /**
     * @return true if application is on background
     * */
    public static boolean isApplicationOnBackground()
    {
        return stateCounter == 0;
    }

    //to be called on each Activity onStart()
    public static void activityStarted()
    {
        stateCounter++;
    }

    //to be called on each Activity onStop()
    public static void activityStopped()
    {
        stateCounter--;
    }
}

Ahora en cada actividad de nuestro programa, debemos anular onStart() y onStop()e incremento / decremento como se muestra a continuación:

@Override
public void onStart()
{
    super.onStart();
    Utilities.activityStarted();
}

@Override
public void onStop()
{
    Utilities.activityStopped();
    if(Utilities.isApplicationOnBackground())
    {
        //you should want to check here if your application is on background
    }
    super.onStop();
}

Con esta lógica, hay 2 casos posibles:

  1. stateCounter = 0 : El número de paradas es igual al número de Actividades iniciadas, lo que significa que la aplicación se ejecuta en segundo plano.
  2. stateCounter > 0 : El número de iniciados es mayor que el número de detenidos, lo que significa que la aplicación se está ejecutando en primer plano.

Aviso: stateCounter < 0 significaría que hay más Actividades detenidas en lugar de iniciadas, lo cual es imposible. Si encuentra este caso, significa que no está aumentando / disminuyendo el contador como debería.

Estás listo para partir Debería verificar si su aplicación está en segundo plano en el interior onStop().

Menelaos Kotsollaris
fuente
Me muevo if(Utilities.isApplicationOnBackground()) …a Utilities. Porque de lo contrario, solo una actividad específica reaccionará al evento.
Nombre para mostrar
10

No hay forma, salvo que lo rastree usted mismo, para determinar si alguna de sus actividades es visible o no. Quizás debería considerar hacer una nueva pregunta de StackOverflow, explicando qué es lo que está tratando de lograr a partir de una experiencia de usuario, para que quizás podamos darle ideas de implementación alternativas.

CommonsWare
fuente
2
En Android, tenemos una configuración llamada "Datos de fondo". Esta configuración desactiva cualquier conexión de datos en segundo plano cuando la aplicación se ejecuta en segundo plano. Quiero implementar la alternancia de "Datos de fondo" para mi aplicación, por lo que cuando ninguna de mis actividades sea visible para el usuario, me gustaría que mi servicio deje de transferir datos, pero en el momento en que se reanude una de mis actividades, me gustaría reanudar la transferencia de datos
cppdev
1
@cppdev: Con suerte, la "transferencia de datos" está siendo realizada por a Service. Si es así, haga que sus actividades notifiquen al servicio a medida que aparecen y desaparecen. Si Servicedetermina que no hay actividades visibles y permanece así durante cierto tiempo, detenga la transferencia de datos en el siguiente punto de detención lógico. Sí, esto requerirá un código para cada una de sus actividades, pero en este momento, eso es inevitable AFAIK.
CommonsWare
1
Si desea evitar copiar y pegar el código común entre todas sus actividades, puede crear una clase que MyActivityClassherede Activitye implemente los métodos del ciclo de vida, y haga que todas sus actividades hereden deMyActivityClass . Esto no hay trabajo para PreferenceActivityo MapActivityembargo (véase esta pregunta )
Guillaume Brunerie
@CommonsWare lo había intentado con OnPause () onResume () que está activo o no, pero si mi aplicación Ver dosen't en pantalla de la vista si se ejecuta en segundo plano cómo comprobar si está activo o no
Manoj
@CommonsWare lo había intentado con OnPause () onResume () que está activo o no, pero si mi aplicación Ver dosen't en pantalla de la vista si se ejecuta en segundo plano cómo comprobar si está activo o no
Manoj
5

Puede usar ComponentCallbacks2 para detectar si la aplicación está en segundo plano. Por cierto, esta devolución de llamada solo está disponible en API Nivel 14 (Ice Cream Sandwich) y superiores.

Recibirá una llamada al método:

public abstract void onTrimMemory (int level)

si el nivel es ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN entonces la aplicación está en segundo plano.

Puede implementar esta interfaz para un activity, service, etc.

public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 {
   @Override
   public void onConfigurationChanged(final Configuration newConfig) {

   }

   @Override
   public void onLowMemory() {

   }

   @Override
   public void onTrimMemory(final int level) {
     if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
        // app is in background
     }
   }
}
Rohit Arya
fuente
1
He intentado su respuesta pero no es tan confiable. la devolución de llamada onTrimMemory no se activará cuando la pantalla esté bloqueada ni al presionar el botón "encendido" para bloquear la pantalla. Tampoco siempre devolverá TRIM_MEMORY_UI_HIDDEN si su aplicación está visible y abre otra aplicación a través de una notificación de barra de estado. La única solución confiable es implementar ActivityLifecycleCallbacks y ajustarlo a los casos de uso.
Velval
4

A partir de la respuesta @Cornstalks se incluyen un par de funciones útiles.

Características adicionales:

  • introdujo el patrón singleton para que pueda hacer esto en cualquier lugar de la aplicación: AppLifecycleHandler.isApplicationVisible () y AppLifecycleHandler.isApplicationInForeground ()
  • manejo adicional de eventos duplicados (ver comentarios // tomar alguna medida sobre el cambio de visibilidad y // tomar alguna medida sobre el cambio en primer plano)

App.java

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
    }
}

AppLifecycleHandler.java

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
    private int resumed;
    private int started;

    private final String DebugName = "AppLifecycleHandler";

    private boolean isVisible = false;
    private boolean isInForeground = false;

    private static AppLifecycleHandler instance;

    public static AppLifecycleHandler getInstance() {
        if (instance == null) {
            instance = new AppLifecycleHandler();
        }

        return instance;
    }

    private AppLifecycleHandler() {
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {
        ++resumed;
        android.util.Log.w(DebugName, "onActivityResumed -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
        setForeground((resumed > 0));
    }

    @Override
    public void onActivityPaused(Activity activity) {
        --resumed;
        android.util.Log.w(DebugName, "onActivityPaused -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
        setForeground((resumed > 0));
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    }

    @Override
    public void onActivityStarted(Activity activity) {
        ++started;
        android.util.Log.w(DebugName, "onActivityStarted -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
        setVisible((started > 0));
    }

    @Override
    public void onActivityStopped(Activity activity) {
        --started;
        android.util.Log.w(DebugName, "onActivityStopped -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
        setVisible((started > 0));
    }

    private void setVisible(boolean visible) {
        if (isVisible == visible) {
            // no change
            return;
        }

        // visibility changed
        isVisible = visible;
        android.util.Log.w(DebugName, "App Visiblility Changed -> application is visible: " + isVisible);

        // take some action on change of visibility
    }

    private void setForeground(boolean inForeground) {
        if (isInForeground == inForeground) {
            // no change
            return;
        }

        // in foreground changed
        isInForeground = inForeground;
        android.util.Log.w(DebugName, "App In Foreground Changed -> application is in foreground: " + isInForeground);

        // take some action on change of in foreground

    }

    public static boolean isApplicationVisible() {
        return AppLifecycleHandler.getInstance().started > 0;
    }

    public static boolean isApplicationInForeground() {
        return AppLifecycleHandler.getInstance().resumed > 0;
    }
}
seb
fuente
3

La mejor solución que se me ocurrió utiliza temporizadores.

Ha iniciado un temporizador en onPause () y cancela el mismo temporizador en onResume (), hay 1 instancia del temporizador (generalmente definido en la clase Aplicación). El temporizador en sí está configurado para ejecutar un Runnable después de 2 segundos (o cualquier intervalo que considere apropiado), cuando se activa el temporizador, establece una bandera que marca la aplicación como en segundo plano.

En el método onResume () antes de cancelar el temporizador, puede consultar el indicador de fondo para realizar cualquier operación de inicio (por ejemplo, iniciar descargas o habilitar servicios de ubicación).

Esta solución le permite tener varias actividades en la pila posterior y no requiere ningún permiso para implementar.

Esta solución funciona bien si también usa un bus de eventos, ya que su temporizador simplemente puede disparar un evento y varias partes de su aplicación pueden responder en consecuencia.

Andrew Kelly
fuente
Estoy empezando a pensar que esta es la mejor (aunque desafortunada) solución
dhaag23
Sí, esta es la mejor solución que he logrado también. Necesitaba detener el escaneo de bluetooth cuando la aplicación no estaba en primer plano, pero no podía simplemente usar onpause o detener o destruir porque no quería detener y comenzar constantemente cuando el usuario navegaba por la aplicación.
CaptRespect
3

Si activa la configuración del desarrollador "No mantener actividades", compruebe que solo el recuento de actividades creadas no es suficiente. Debe verificar también isSaveInstanceState . Mi método personalizado esAplicaciónRunning () es ejecutar la aplicación de Android:

Aquí mi código de trabajo:

public class AppLifecycleService implements Application.ActivityLifecycleCallbacks {
    private int created;
    private boolean isSaveInstanceState;
    private static AppLifecycleService instance;

    private final static String TAG = AppLifecycleService.class.getName();

    public static AppLifecycleService getInstance() {
        if (instance == null) {
            instance = new AppLifecycleService();
        }
        return instance;
    }

    public static boolean isApplicationRunning() {
        boolean isApplicationRunning = true;
        if (getCountCreatedActvities() == 0 && !isSaveInstanceState()) {
            isApplicationRunning = false;
        }
        return isApplicationRunning;
    }

    public static boolean isSaveInstanceState() {
        return AppLifecycleService.getInstance().isSaveInstanceState;
    }

    public static int getCountCreatedActvities() {
        return AppLifecycleService.getInstance().created;
    }

    private AppLifecycleService() {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        this.isSaveInstanceState = true;
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        ++created;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        --created;
    }

    @Override
    public void onActivityResumed(Activity activity) {   }

    @Override
    public void onActivityPaused(Activity activity) { }


    @Override
    public void onActivityStarted(Activity activity) { }

    @Override
    public void onActivityStopped(Activity activity) { }        

}
un_suscriptor
fuente
3

La única solución correcta:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        MyApp.mainActivity = this;
        super.onCreate(savedInstanceState);
        ...
    }

MyApp.java:

public class MyApp extends Application implements LifecycleObserver {

    public static MainActivity mainActivity = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onAppBackgrounded() {
        // app in background
        if (mainActivity != null) {
            ...
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onAppForegrounded() {
        // app in foreground
        if (mainActivity != null) {
            ...
        }
    }

}
Dmitry
fuente
No veo cómo esta solución puede darme una respuesta a una pregunta simple dentro de una declaración IF sobre mi actividad (o fragmento) si mi aplicación está en segundo plano o en primer plano. ¿Dónde está la declaración "SI"?
Ekashking
2

Para aprovechar lo que han dicho CommonsWare y Key, quizás podría extender la clase de aplicación y hacer que todas sus actividades llamen a eso en sus métodos onPause / onResume. Esto le permitiría saber qué Actividad (es) están visibles, pero esto probablemente podría manejarse mejor.

¿Puedes explicar exactamente lo que tienes en mente? Cuando dice que se ejecuta en segundo plano, ¿quiere decir simplemente que su aplicación aún está en la memoria aunque no esté actualmente en la pantalla? ¿Ha considerado usar los Servicios como una forma más persistente de administrar su aplicación cuando no está enfocada?

Dan
fuente
En Android, tenemos una configuración llamada "Datos de fondo". Esta configuración desactiva cualquier conexión de datos en segundo plano cuando la aplicación se ejecuta en segundo plano. Quiero implementar la alternancia de "Datos de fondo" para mi aplicación, por lo que cuando ninguna de mis actividades sea visible para el usuario, me gustaría que mi servicio deje de transferir datos, pero en el momento en que se reanude una de mis actividades, me gustaría reanudar la transferencia de datos
cppdev
1
Applicationno tiene onPause()o onResume().
CommonsWare
1
@CommonsWare Tienes razón, me refería a cada Actividad individual que contactaba a la Aplicación en su pausa / currículum. Esta es básicamente la idea que acaba de compartir en el comentario a su respuesta, aunque utilizó los Servicios, lo que me imagino que es un movimiento más inteligente.
Dan
2

Hice mi propia implementación de ActivityLifecycleCallbacks. Estoy usando SherlockActivity, pero para la clase de actividad normal podría funcionar.

Primero, estoy creando una interfaz que tiene todos los métodos para rastrear el ciclo de vida de las actividades:

public interface ActivityLifecycleCallbacks{
    public void onActivityStopped(Activity activity);
    public void onActivityStarted(Activity activity);
    public void onActivitySaveInstanceState(Activity activity, Bundle outState);
    public void onActivityResumed(Activity activity);
    public void onActivityPaused(Activity activity);
    public void onActivityDestroyed(Activity activity);
    public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}

En segundo lugar, implementé esta interfaz en la clase de mi aplicación:

public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{

    @Override
    public void onCreate() {
        super.onCreate();           
    }

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());

    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
    }
}

Tercero, estoy creando una clase que se extiende desde SherlockActivity:

public class MySherlockActivity extends SherlockActivity {

    protected MyApplication nMyApplication;

    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        nMyApplication = (MyApplication) getApplication();
        nMyApplication.onActivityCreated(this, savedInstanceState);
    }

    protected void onResume() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityResumed(this);
        super.onResume();

    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityPaused(this);
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityDestroyed(this);
        super.onDestroy();
    }

    @Override
    protected void onStart() {
        nMyApplication.onActivityStarted(this);
        super.onStart();
    }

    @Override
    protected void onStop() {
        nMyApplication.onActivityStopped(this);
        super.onStop();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        nMyApplication.onActivitySaveInstanceState(this, outState);
        super.onSaveInstanceState(outState);
    }   
}

Cuarto, todas las clases que se extienden desde SherlockActivity, reemplacé por MySherlockActivity:

public class MainActivity extends MySherlockActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

}

Ahora, en el logcat verá los registros programados en la implementación de la interfaz realizada en MyApplication.

ClarkXP
fuente
1

La actividad se detiene cuando aparece un cuadro de diálogo, por lo que todas las soluciones recomendadas son medias soluciones. También necesita crear ganchos para los diálogos.

konmik
fuente
1

Documentos oficiales:

El sistema distingue entre aplicaciones en primer plano y en segundo plano. (La definición de fondo para fines de limitaciones de servicio es distinta de la definición utilizada por la administración de memoria; una aplicación puede estar en segundo plano en lo que respecta a la administración de memoria , pero en primer plano en lo que respecta a su capacidad para iniciar servicios). se considera que está en primer plano si se cumple alguna de las siguientes condiciones:

  1. Tiene una actividad visible, ya sea que la actividad se inicie o se detenga.
  2. Cuenta con un servicio de primer plano.
  3. Otra aplicación en primer plano está conectada a la aplicación, ya sea vinculando a uno de sus servicios o haciendo uso de uno de sus proveedores de contenido. Por ejemplo, la aplicación está en primer plano si otra aplicación se une a su:
    • YO ME
    • Servicio de papel tapiz
    • Oyente de notificación
    • Servicio de voz o texto

Si ninguna de esas condiciones es verdadera, se considera que la aplicación está en segundo plano.

Uddhav Gautam
fuente
0

Otra solución para esta publicación anterior (para aquellos que podría ayudar):


<application android:name=".BaseApplication" ... >

public class BaseApplication extends Application {

    private class Status {
        public boolean isVisible = true;
        public boolean isFocused = true;
    }

    private Map<Activity, Status> activities;

    @Override
    public void onCreate() {
        activities = new HashMap<Activity, Status>();
        super.onCreate();
    }

    private boolean hasVisibleActivity() {
        for (Status status : activities.values())
            if (status.isVisible)
                return true;
        return false;
    }

    private boolean hasFocusedActivity() {
        for (Status status : activities.values())
            if (status.isFocused)
                return true;
        return false;
    }

    public void onActivityCreate(Activity activity, boolean isStarting) {
        if (isStarting && activities.isEmpty())
            onApplicationStart();
        activities.put(activity, new Status());
    }

    public void onActivityStart(Activity activity) {
        if (!hasVisibleActivity() && !hasFocusedActivity())
            onApplicationForeground();
        activities.get(activity).isVisible = true;
    }

    public void onActivityWindowFocusChanged(Activity activity, boolean hasFocus) {
        activities.get(activity).isFocused = hasFocus;
    }

    public void onActivityStop(Activity activity, boolean isFinishing) {
        activities.get(activity).isVisible = false;
        if (!isFinishing && !hasVisibleActivity() && !hasFocusedActivity())
            onApplicationBackground();
    }

    public void onActivityDestroy(Activity activity, boolean isFinishing) {
        activities.remove(activity);
        if(isFinishing && activities.isEmpty())
            onApplicationStop();
    }

    private void onApplicationStart() {Log.i(null, "Start");}
    private void onApplicationBackground() {Log.i(null, "Background");}
    private void onApplicationForeground() {Log.i(null, "Foreground");}
    private void onApplicationStop() {Log.i(null, "Stop");}

}

public class MyActivity extends BaseActivity {...}

public class BaseActivity extends Activity {

    private BaseApplication application;

    @Override
    protected void onCreate(Bundle state) {
        application = (BaseApplication) getApplication();
        application.onActivityCreate(this, state == null);
        super.onCreate(state);
    }

    @Override
    protected void onStart() {
        application.onActivityStart(this);
        super.onStart();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        application.onActivityWindowFocusChanged(this, hasFocus);
        super.onWindowFocusChanged(hasFocus);
    }

    @Override
    protected void onStop() {
        application.onActivityStop(this, isFinishing());
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        application.onActivityDestroy(this, isFinishing());
        super.onDestroy();
    }

}
alex
fuente
0

Vea el comentario en la función onActivityDestroyed.

Funciona con SDK target versión 14>:

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {

    public static int active = 0;

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
        active--;

        // if active var here ever becomes zero, the app is closed or in background
        if(active == 0){
            ...
        }

    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
        active++;
    }
}
ramden
fuente
0

Debe usar una preferencia compartida para almacenar la propiedad y actuar sobre ella utilizando el enlace de servicio de sus actividades. Si usa solo el enlace, (eso nunca es usar startService), entonces su servicio se ejecutará solo cuando lo enlace, (bind onResume y unbind onPause) que haría que se ejecute solo en primer plano, y si desea trabajar en En segundo plano, puede utilizar el servicio regular de inicio y parada.

Ofek Ron
fuente
0

Creo que esta pregunta debería ser más clara. ¿Cuando? ¿Dónde? ¿Cuál es su situación específica que desea conocer si su aplicación está en segundo plano?

Acabo de presentar mi solución en mi camino.
Lo hago al usar el campo "importancia" de la RunningAppProcessInfoclase en el onStopmétodo de cada actividad en mi aplicación, lo que se puede lograr simplemente proporcionando una BaseActivityextensión para otras actividades que implementa el onStopmétodo para verificar el valor de "importancia". Aquí está el código:

public static boolean isAppRunning(Context context) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> appProcesses = activityManager
        .getRunningAppProcesses();
    for (RunningAppProcessInfo appProcess : appProcesses) {
        if (appProcess.processName.equals(context.getPackageName())) {
            if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {
                return true;
            } 
        }
    }
    return false;
}
wxf04125
fuente
Esta no es una solución recomendada como se indica en la respuesta de @ Idolon.
CoolMind
0

Recomiendo leer esta página: http://developer.android.com/reference/android/app/Activity.html

En resumen, su actividad ya no es visible después de onStop()haber sido llamada.

Llave
fuente
3
Tengo alrededor de 10 actividades en mi aplicación. Entonces quiero saber si ninguno de ellos es visible para el usuario. En general, quiero saber si mi aplicación en su conjunto se está ejecutando en segundo plano
cppdev
Entonces haces un seguimiento de todos los 10-ish. O, como sugirió CommonsWare, explique lo que está tratando de hacer.
Clave del
3
Esto no es correcto Su actividad es visible hasta onStop; entre onPausey onStopes visible , pero no en primer plano .
nickgrim
@nickgrim: ¿qué no es correcto? Dije que una actividad ya no es visible después de que onStop()se llama, que está alineada con lo que escribió.
Clave
@Key: Originalmente dijiste hasta que onPausese llama: una edición reciente te ha corregido.
nickgrim
0

¿Qué pasa con el uso de getApplicationState (). IsInForeground ()?

Jonathan
fuente
0

En mi opinión, muchas respuestas introducen una gran carga de código y traen mucha complejidad y no legibilidad.

Cuando la gente pregunta sobre SO cómo comunicar entre una Servicey una Activity, por lo general consejos para utilizar la LocalBroadcastManager .


¿Por qué?

Bueno, citando los documentos:

  • Sabe que los datos que está transmitiendo no saldrán de su aplicación, por lo que no debe preocuparse por la pérdida de datos privados.

  • No es posible que otras aplicaciones envíen estas transmisiones a su aplicación, por lo que no debe preocuparse por tener agujeros de seguridad que puedan explotar.

  • Es más eficiente que enviar una transmisión global a través del sistema.

No en los documentos:

  • No requiere bibliotecas externas.
  • El código es mínimo
  • Es rápido de implementar y comprender
  • No hay devoluciones de llamada auto-implementadas personalizadas / ultra-singleton / patrón intraproceso en absoluto ...
  • No hay referencias fuertes sobre Activity, Application, ...

Descripción

Por lo tanto, desea verificar si alguno de ellos Activityestá actualmente en primer plano. Usualmente haces eso en un Service, o tuApplication clase.

Esto significa que sus Activityobjetos se convierten en el emisor de una señal (estoy encendido / apagado). Tu Service, por otro lado, se convierte en elReceiver .

Hay dos momentos en que suActivity te dice si está en primer plano o en segundo plano (sí, solo dos ... no 6).

Cuando Activityentra en primer plano, onResume()se activa el método (también llamado después onCreate()).

Cuando Activityva en la parte de atrás, onPause()se llama.

Estos son los momentos en los que Activitydebe enviar la señal a suService para describir su estado.

En caso de múltiples Activity, recuerde elActivity pasa al fondo primero, luego otro entra en primer plano.

Entonces la situación sería: *

Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON

El Service/ Applicationsimplemente seguirá escuchando esas señales y actuará en consecuencia.


Código (TLDR)

Su Servicedebe implementar un BroadcastReceiverfin de escuchar señales.

this.localBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // received data if Activity is on / off
    }
}

public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL") 

Registra el ReceiverenService::onCreate()

@Override
protected void onCreate() {
    LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}

Anular el registro en Service::onDestroy()

@Override
protected void onDestroy() {
    // I'm dead, no need to listen to anything anymore.
    LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}

Ahora tu Activitydebe comunicar su estado.

En Activity::onResume()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

En Activity::onPause()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

Una situación muy, muy común.

Desarrollador: Quiero enviar datos desde mi Servicey actualizar el Activity. ¿Cómo verifico si el Activityestá en primer plano?

Por lo general, no es necesario verificar si Activityestá en primer plano o no. Simplemente envíe los datos a través LocalBroadcastManagerde su Service. Si Activityestá activado, responderá y actuará.

Para esta situación muy común, el se Serviceconvierte en el remitente y el Activityimplementa el BroadcastReceiver.

Entonces, crea un Receiveren tu Activity. Regístrese onResume()y anule el registro onPause(). No es necesario utilizar los otros métodos de ciclo de vida. .

Defina el Receivercomportamiento en onReceive()(actualice ListView, haga esto, haga eso, ...).

De esta manera Activity, solo escuchará si está en primer plano y no pasará nada si está en la parte posterior o si se destruye.

En caso de múltiples Activity, lo que Activityesté encendido responderá (si también implementan Receiver).

Si todos están en segundo plano, nadie responderá y la señal simplemente se perderá.

Envíe los datos desde la Servicevía Intent(consulte el código anterior) especificando la ID de la señal.


Marko Pacak
fuente
0
fun isAppInForeground(): Boolean {
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager ?: return false

    val appProcesses = activityManager.runningAppProcesses ?: return false

    val packageName = packageName
    for (appProcess in appProcesses) {
        if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName) {
            return true
        }
    }

    return false
}
ayoub laaziz
fuente
0

Ninguna de las respuestas se ajusta al caso específico si se busca saber si una actividad específica está en primer plano y si usted es un SDK sin acceso directo a la Aplicación. Para mí, estaba en el hilo de fondo después de recibir una notificación automática para un nuevo mensaje de chat y solo quería mostrar una notificación del sistema si la pantalla de chat no estaba en primer plano.

Usando ActivityLifecycleCallbackseso como se ha recomendado en otras respuestas, he creado una pequeña clase de utilidad que alberga la lógica de si MyActivityestá en primer plano o no.

class MyActivityMonitor(context: Context) : Application.ActivityLifecycleCallbacks {

private var isMyActivityInForeground = false

init {
    (context.applicationContext as Application).registerActivityLifecycleCallbacks(this)
}

fun isMyActivityForeground() = isMyActivityInForeground

override fun onActivityPaused(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = false
    }
}

override fun onActivityResumed(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = true
    }
}

}

scottyab
fuente
-1

En mis actividades onResume y onPause escribo un booleano isVisible en SharedPrefences.

    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    Editor editor = sharedPrefs.edit();
    editor.putBoolean("visible", false);
    editor.commit();

Y léalo en otro lugar cuando sea necesario a través de,

    // Show a Toast Notification if App is not visible (ie in background. Not running, etc) 
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
    if(!sharedPrefs.getBoolean("visible", true)){...}

Tal vez no sea elegante, pero funciona para mí ...

mtwagner
fuente
-1

Puede ser demasiado tarde para responder, pero si alguien viene de visita, entonces esta es la solución que sugiero. Para mostrar tostadas y notificaciones cuando el usuario está en BG. 2. Para realizar algunas tareas por primera vez, el usuario proviene de BG, como una encuesta, redibujar, etc.

La solución de Idolon y otros se encarga de la primera parte, pero no para la segunda. Si hay varias actividades en su aplicación, y el usuario está cambiando entre ellas, entonces cuando esté en la segunda actividad, la bandera visible será falsa. Por lo tanto, no se puede usar de manera determinista.

Hice algo que fue sugerido por CommonsWare, "Si el Servicio determina que no hay actividades visibles, y permanece así por un período de tiempo , detenga la transferencia de datos en el siguiente punto lógico de detención".

La línea en negrita es importante y se puede usar para lograr el segundo elemento. Entonces, lo que hago es que una vez que obtengo el onActivityPaused (), no cambie el visible a falso directamente, en su lugar tenga un temporizador de 3 segundos (ese es el máximo para que se inicie la próxima actividad), y si no hay onActivityResumed ( ) llame en los próximos 3 segundos, cambie visible a falso. Del mismo modo en onActivityResumed () si hay un temporizador, lo cancelo. En resumen, lo visible se convierte en isAppInBackground.

Lo sentimos, no puedo copiar y pegar el código ...

Sriram
fuente
-3

Me gustaría recomendarle que use otra forma de hacer esto.

Supongo que desea mostrar la pantalla de inicio mientras se inicia el programa, si ya se está ejecutando en el back-end, no lo muestre.

Su aplicación puede escribir continuamente la hora actual en un archivo específico. Mientras su aplicación se está iniciando, verifique la última marca de tiempo, si current_time-last_time> el rango de tiempo que especificó para escribir la última hora, significa que su aplicación se detiene, ya sea eliminada por el sistema o el propio usuario.

usuario2322866
fuente