Estoy haciendo una notificación de la barra de estado en mi aplicación de Android que es activada por c2dm. No quiero mostrar la notificación si la aplicación se está ejecutando. ¿Cómo se determina si la aplicación se está ejecutando y está en primer plano?
android
android-activity
Andrew Thomas
fuente
fuente
Respuestas:
Haga una variable global como
private boolean mIsInForegroundMode;
y asigne unfalse
valor enonPause()
y untrue
valor enonResume()
.Código de muestra:
private boolean mIsInForegroundMode; @Override protected void onPause() { super.onPause(); mIsInForegroundMode = false; } @Override protected void onResume() { super.onResume(); mIsInForegroundMode = true; } // Some function. public boolean isInForeground() { return mIsInForegroundMode; }
fuente
Alternativamente, puede verificar
ActivityManager
qué tareas se están ejecutando porgetRunningTasks
método. Luego verifique con la primera tarea (tarea en primer plano) en la Lista de tareas devuelta, si es su tarea.Aquí está el ejemplo de código:
public Notification buildNotification(String arg0, Map<String, String> arg1) { ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE); List<RunningTaskInfo> services = activityManager .getRunningTasks(Integer.MAX_VALUE); boolean isActivityFound = false; if (services.get(0).topActivity.getPackageName().toString() .equalsIgnoreCase(appContext.getPackageName().toString())) { isActivityFound = true; } if (isActivityFound) { return null; } else { // write your code to build a notification. // return the notification you built here } }
Y no olvide agregar el
GET_TASKS
permiso en el archivo manifest.xml para poder ejecutar elgetRunningTasks()
método en el código anterior:<uses-permission android:name="android.permission.GET_TASKS" />
p / s: Si está de acuerdo de esta manera, tenga en cuenta que este permiso ahora está obsoleto.
fuente
toString()
una cadena devuelta porgetPackageName()
es redundante. Además, como solo nos interesa la primera tarea devuelta porgetRunningTasks()
, podemos pasar en1
lugar deInteger.MAX_VALUE
.Esta es una publicación bastante antigua pero aún bastante relevante. La solución aceptada anteriormente puede funcionar, pero es incorrecta. 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, un administrador de tareas, etc.
Sí, hay una lista guardada en la memoria para estas cosas. Sin embargo, está apagado en otro proceso, administrado por hilos que se ejecutan por separado de los suyos, y no es algo con lo que pueda contar (a) ver a tiempo para tomar la decisión correcta o (b) tener una imagen consistente para cuando regrese. Además, la decisión sobre cuál es la "próxima" actividad a la que ir siempre se realiza en el punto en el que se producirá el cambio, y no es hasta ese punto exacto (donde el estado de actividad se bloquea brevemente para realizar el cambio) cuando realmente sé por tal 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.
La solución correcta es implementar: ActivityLifeCycleCallbacks .
Básicamente, esto necesita una clase de aplicación y el controlador se puede configurar allí para identificar el estado de sus actividades en la aplicación.
fuente
onPause
yonResume
utilizado en la respuesta aceptada?Como dice Vinay, probablemente la mejor solución (para admitir versiones más nuevas de Android, 14+) es usarla
ActivityLifecycleCallbacks
en laApplication
implementación de la clase.package com.telcel.contenedor.appdelegate; import android.app.Activity; import android.app.Application.ActivityLifecycleCallbacks; import android.os.Bundle; /** Determines global app lifecycle states. * * The following is the reference of activities states: * * The <b>visible</b> lifetime of an activity happens between a call to onStart() * until a corresponding call to onStop(). During this time the user can see the * activity on-screen, though it may not be in the foreground and interacting with * the user. The onStart() and onStop() methods can be called multiple times, as * the activity becomes visible and hidden to the user. * * The <b>foreground</b> lifetime of an activity happens between a call to onResume() * until a corresponding call to onPause(). During this time the activity is in front * of all other activities and interacting with the user. An activity can frequently * go between the resumed and paused states -- for example when the device goes to * sleep, when an activity result is delivered, when a new intent is delivered -- * so the code in these methods should be fairly lightweight. * * */ public class ApplicationLifecycleManager implements ActivityLifecycleCallbacks { /** Manages the state of opened vs closed activities, should be 0 or 1. * It will be 2 if this value is checked between activity B onStart() and * activity A onStop(). * It could be greater if the top activities are not fullscreen or have * transparent backgrounds. */ private static int visibleActivityCount = 0; /** Manages the state of opened vs closed activities, should be 0 or 1 * because only one can be in foreground at a time. It will be 2 if this * value is checked between activity B onResume() and activity A onPause(). */ private static int foregroundActivityCount = 0; /** Returns true if app has foreground */ public static boolean isAppInForeground(){ return foregroundActivityCount > 0; } /** Returns true if any activity of app is visible (or device is sleep when * an activity was visible) */ public static boolean isAppVisible(){ return visibleActivityCount > 0; } public void onActivityCreated(Activity activity, Bundle bundle) { } public void onActivityDestroyed(Activity activity) { } public void onActivityResumed(Activity activity) { foregroundActivityCount ++; } public void onActivityPaused(Activity activity) { foregroundActivityCount --; } public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } public void onActivityStarted(Activity activity) { visibleActivityCount ++; } public void onActivityStopped(Activity activity) { visibleActivityCount --; } }
Y en el
onCreate()
método de aplicación :registerActivityLifecycleCallbacks(new ApplicationLifecycleManager());
Entonces
ApplicationLifecycleManager.isAppVisible()
oApplicationLifecycleManager.isAppInForeground()
se usaría para conocer el estado deseado.fuente
Desde API 16 puedes hacerlo así:
static boolean shouldShowNotification(Context context) { RunningAppProcessInfo myProcess = new RunningAppProcessInfo(); ActivityManager.getMyMemoryState(myProcess); if (myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND) return true; KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); // app is in foreground, but if screen is locked show notification anyway return km.inKeyguardRestrictedInputMode(); }
fuente
Para su información, si usa la solución Gadenkan (¡que es genial!) No olvide agregar
<uses-permission android:name="android.permission.GET_TASKS" />
al manifiesto.
fuente
Versión ligeramente limpia de la solución de Gadenkan . Ponlo en cualquier actividad, o tal vez una clase base para todas tus actividades.
protected boolean isRunningInForeground() { ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1); if (tasks.isEmpty()) { return false; } String topActivityName = tasks.get(0).topActivity.getPackageName(); return topActivityName.equalsIgnoreCase(getPackageName()); }
Para poder llamar
getRunningTasks()
, debe agregar esto en suAndroidManifest.xml
:<uses-permission android:name="android.permission.GET_TASKS"/>
Sin
ActivityManager.getRunningTasks()
embargo, tenga en cuenta lo que dice Javadoc:Actualización (febrero de 2015)
¡Tenga en cuenta que
getRunningTasks()
quedó obsoleto en el nivel de API 21 !Entonces, lo que escribí antes es aún más relevante:
En muchos casos, probablemente pueda encontrar una solución mejor. Por ejemplo, hacer algo en
onPause()
yonResume()
, quizás, en una BaseActivity para todas sus actividades.(En nuestro caso, no queríamos que se lanzara una actividad de alerta fuera de línea si no estamos en primer plano, por lo que en BaseActivity
onPause()
simplemente cancelamos la suscripción a la señal de RxJava queSubscription
escucha "se desconectó").fuente
Siguiendo la respuesta de Gadenkan, necesitaba algo como esto para poder saber si mi aplicación no se estaba ejecutando en primer plano, pero necesitaba algo que fuera para toda la aplicación y no requiriera que configurara / desarmara banderas en toda mi aplicación.
El código de Gadenkan casi dio en el clavo, pero no estaba en mi propio estilo y sentí que podría ser más ordenado, por lo que en mi aplicación está condensado en esto.
if (!context.getPackageName().equalsIgnoreCase(((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1).get(0).topActivity.getPackageName())) { // App is not in the foreground }
(Nota al margen: puede quitar el! Si desea que el cheque funcione al revés)
Aunque con este enfoque necesitas el
GET_TASKS
permiso.fuente
A partir de la versión 26 de la biblioteca de soporte, puede usar ProcessLifecycleOwner para determinar el estado actual de la aplicación, simplemente agréguela a sus dependencias 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 }
Ahora puede consultar
ProcessLifecycleOwner
cada vez que desee verificar el estado de la aplicación, por ejemplo, para verificar si la aplicación se está ejecutando en primer plano, solo tiene que hacer esto:boolean isAppInForeground = ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED); if(!isAppInForeground) //Show Notification in status bar
fuente
implementation 'androidx.lifecycle:lifecycle-process:2.2.0'
en su proyecto gradle.Según las diversas respuestas y comentarios, aquí hay una versión más insertada que puede agregar a una clase de ayuda:
public static boolean isAppInForeground(Context context) { List<RunningTaskInfo> task = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)) .getRunningTasks(1); if (task.isEmpty()) { return false; } return task .get(0) .topActivity .getPackageName() .equalsIgnoreCase(context.getPackageName()); }
Como se menciona en otras respuestas, debe agregar el siguiente permiso a su
AndroidManifest.xml
.<uses-permission android:name="android.permission.GET_TASKS"/>
fuente
Me gustaría agregar que una forma más segura de hacer esto, que verificar si su aplicación está en segundo plano antes de crear una notificación, es simplemente deshabilitar y habilitar el receptor de transmisión onPause () y onResume () respectivamente.
Este método le brinda más control sobre la lógica de la aplicación real y no es probable que cambie en el futuro.
@Override protected void onPause() { unregisterReceiver(mHandleMessageReceiver); super.onPause(); } @Override protected void onResume() { super.onResume(); registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION)); }
fuente
Encontré una forma más simple y precisa de verificar si la aplicación está en primer plano o en segundo plano asignando las actividades a booleanos.
Consulte la esencia completa aquí
fuente
Aquí está el código para una solución simple y agradable descrita anteriormente por @ user2690455. Aunque parece un poco detallado, verá que en general es bastante ligero
En mi caso también usamos AppCompatActivity, así que tenía que tener 2 clases base.
public class BaseActivity extends Activity { /** * Let field be set only in base class * All callers must use accessors, * and then it's not up to them to manage state. * * Making it static since .. * 1. It needs to be used across two base classes * 2. It's a singleton state in the app */ private static boolean IS_APP_IN_BACKGROUND = false; @Override protected void onResume() { super.onResume(); BaseActivity.onResumeAppTracking(this); BaseActivity.setAppInBackgroundFalse(); } @Override protected void onStop() { super.onStop(); BaseActivity.setAppInBackgroundTrue(); } @Override protected void onPause() { super.onPause(); BaseActivity.setAppInBackgroundFalse(); } protected static void onResumeAppTracking(Activity activity) { if (BaseActivity.isAppInBackground()) { // do requirements for returning app to foreground } } protected static void setAppInBackgroundFalse() { IS_APP_IN_BACKGROUND = false; } protected static void setAppInBackgroundTrue() { IS_APP_IN_BACKGROUND = true; } protected static boolean isAppInBackground() { return IS_APP_IN_BACKGROUND; } }
fuente
Esto es útil solo cuando desea realizar alguna acción justo cuando comienza su actividad y es donde desea verificar si la aplicación está en primer plano o en segundo plano.
En lugar de usar el Administrador de actividades, hay un truco simple que puede hacer a través del código. Si observa el ciclo de actividad de cerca, el flujo entre dos actividades y el primer plano al fondo es el siguiente. Suponga que A y B son dos actividades.
Cuando la transición de A a B: 1. onPause () de A se llama 2. onResume () de B se llama 3. onStop () de A se llama cuando B se reanuda completamente
Cuando la aplicación pasa a segundo plano: 1. onPause () de A se llama 2. onStop () de A se llama
Puede detectar su evento de fondo simplemente poniendo una bandera en actividad.
Haga una actividad abstracta y extiéndala de sus otras actividades, de modo que no tenga que copiar y pegar el código para todas las demás actividades donde necesite un evento de fondo.
En la actividad abstracta, cree la bandera isAppInBackground.
En el método onCreate (): isAppInBackground = false;
En el método onPause (): isAppInBackground = false;
En el método onStop (): isAppInBackground = true;
Solo necesita verificar en su onResume () si isAppInBackground es verdadero. n después de verificar su bandera, vuelva a configurar isAppInBackground = false
Para la transición entre dos actividades, ya que onSTop () de la primera siempre se llamará después de que se reanude la segunda actividad, el indicador nunca será verdadero y cuando la aplicación esté en segundo plano, se llamará a onStop () de la actividad inmediatamente después de onPause y, por lo tanto, el indicador será verdadero cuando abre la aplicación más tarde.
Sin embargo, hay un escenario más en este enfoque. Si alguna de las pantallas de tu aplicación ya está abierta y pones el móvil inactivo, después de un tiempo, el móvil entrará en modo de suspensión y cuando lo desbloquees, se tratará en un evento de fondo.
fuente
Aquí hay un método que utilizo (y un método de apoyo):
private boolean checkIfAppIsRunningInForeground() { ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); for(ActivityManager.RunningAppProcessInfo appProcessInfo : activityManager.getRunningAppProcesses()) { if(appProcessInfo.processName.contains(this.getPackageName())) { return checkIfAppIsRunningInForegroundByAppImportance(appProcessInfo.importance); } } return false; } private boolean checkIfAppIsRunningInForegroundByAppImportance(int appImportance) { switch (appImportance) { //user is aware of app case ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND: case ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE: return true; //user is not aware of app case ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND: case ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY: case ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE: case ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE: default: return false; } }
fuente
No hay devolución de llamada global para esto, pero para cada actividad es onStop (). No necesitas meterte con un int atómico. Solo tenga un int global con el número de actividades iniciadas, en cada actividad increméntelo en onStart () y decremítelo en onStop ().
Sigue esto
fuente
public static boolean isAppRunning(Context context) { // check with the first task(task in the foreground) // in the returned list of tasks ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<RunningTaskInfo> services = activityManager.getRunningTasks(Integer.MAX_VALUE); if (services.get(0).topActivity.getPackageName().toString().equalsIgnoreCase(context.getPackageName().toString())) { return true; } return false; }
fuente
Los enfoques anteriores mencionados aquí no son óptimos. El enfoque basado en tareas requiere un permiso que podría no ser deseado y el enfoque "booleano" es propenso a errores de modificación simultánea.
El enfoque que uso y que (creo) funciona bastante bien en la mayoría de los casos:
Tener una clase "MainApplication" para realizar un seguimiento del recuento de actividades en AtomicInteger :
import android.app.Application; import java.util.concurrent.atomic.AtomicInteger; public class MainApplication extends Application { static class ActivityCounter { private static AtomicInteger ACTIVITY_COUNT = new AtomicInteger(0); public static boolean isAppActive() { return ACTIVITY_COUNT.get() > 0; } public static void activityStarted() { ACTIVITY_COUNT.incrementAndGet(); } public static void activityStopped() { ACTIVITY_COUNT.decrementAndGet(); } } }
Y cree una clase de actividad base que otras actividades extenderían:
import android.app.Activity; import android.support.annotation.CallSuper; public class TestActivity extends Activity { @Override @CallSuper protected void onStart() { MainApplication.ActivityCounter.activityStarted(); super.onStart(); } @Override @CallSuper protected void onStop() { MainApplication.ActivityCounter.activityStopped(); super.onStop(); } }
fuente