Por antecedentes, ¿quiero decir que ninguna de las actividades de la aplicación es visible actualmente para el usuario?
Por antecedentes, ¿quiero decir que ninguna de las actividades de la aplicación es visible actualmente para el usuario?
Respuestas:
Hay pocas formas de detectar si su aplicación se ejecuta en segundo plano, pero solo una de ellas es completamente confiable:
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.onResume
métodos. Almacenar el estado de "visibilidad" en alguna otra clase. Las buenas opciones son su propia implementación de la Application
o una Service
(también hay algunas variaciones de esta solución si desea verificar la visibilidad de la actividad desde el servicio).
Ejemplo
Implementar Application
clase 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 onPause
y onResume
a cada uno Activity
en el proyecto (puede crear un antepasado común para sus Actividades si lo desea, pero si su actividad ya se ha extendido desde MapActivity
/ ListActivity
etc. 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.
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 deRunningAppProcessInfo
registros. Para determinar si su aplicación está en elRunningAppProcessInfo.importance
campo de verificación de primer plano para la igualdadRunningAppProcessInfo.IMPORTANCE_FOREGROUND
mientrasRunningAppProcessInfo.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á importanciaIMPORTANCE_FOREGROUND
para su tarea, sin importar si realmente está en primer plano o no. Llámelo en el hilo de fondo (por ejemplo, víaAsyncTask
) 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.
Otra solución incorrecta que la biblioteca
Droid-Fu mencionó en una de las respuestas utiliza ActivityManager.getRunningTasks
para su isApplicationBroughtToBackground
método. Vea el comentario de Dianne arriba y tampoco use ese método.
OnStop
solicitud de isActivityVisible
.
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.
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 Activity
en 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 MyLifecycleHandler
se 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 configChanges
para que su actividad no se destruya. Afortunadamente, tuve que configurarconfigChanges
Ya 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()
.
onStop()
después super.onStop()
. No verifique el fondo en onPause()
.
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
companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }
entonces puedes obtener el estado de primer plano conArchLifecycleApp.isForeground()
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 processes
aplicaciones, ¿hay alguna API que podamos lograr con elegancia?
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 ProcessLifecycleOwner
cuando 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);
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;
JobService
para detectar que el servicio se está ejecutando en segundo plano.
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
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.
onPause
, onStop
ni el onResume
evento se llama. Entonces, ¿qué haces si ninguno de estos eventos se dispara?
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 ActivityAonStop()
.
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.xml
etiqueta 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:
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.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()
.
if(Utilities.isApplicationOnBackground()) …
a Utilities
. Porque de lo contrario, solo una actividad específica reaccionará al evento.
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.
Service
. Si es así, haga que sus actividades notifiquen al servicio a medida que aparecen y desaparecen. Si Service
determina 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.
MyActivityClass
herede Activity
e implemente los métodos del ciclo de vida, y haga que todas sus actividades hereden deMyActivityClass
. Esto no hay trabajo para PreferenceActivity
o MapActivity
embargo (véase esta pregunta )
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
}
}
}
A partir de la respuesta @Cornstalks se incluyen un par de funciones útiles.
Características adicionales:
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;
}
}
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.
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) { }
}
La única solución correcta:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
MyApp.mainActivity = this;
super.onCreate(savedInstanceState);
...
}
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) {
...
}
}
}
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?
Application
no tiene onPause()
o onResume()
.
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.
Como ya no se menciona, sugeriré a los lectores que exploren ProcessLifecycleOwner disponible a través de los componentes de Android Architecture
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:
Si ninguna de esas condiciones es verdadera, se considera que la aplicación está en segundo plano.
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();
}
}
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++;
}
}
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.
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 RunningAppProcessInfo
clase en el onStop
método de cada actividad en mi aplicación, lo que se puede lograr simplemente proporcionando una BaseActivity
extensión para otras actividades que implementa el onStop
mé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;
}
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.
onStop
; entre onPause
y onStop
es visible , pero no en primer plano .
onStop()
se llama, que está alineada con lo que escribió.
onPause
se llama: una edición reciente te ha corregido.
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 Service
y 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:
Activity
, Application
, ...Descripción
Por lo tanto, desea verificar si alguno de ellos Activity
está actualmente en primer plano. Usualmente haces eso en un Service
, o tuApplication
clase.
Esto significa que sus Activity
objetos 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 Activity
entra en primer plano, onResume()
se activa el método (también llamado después onCreate()
).
Cuando Activity
va en la parte de atrás, onPause()
se llama.
Estos son los momentos en los que Activity
debe 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
/ Application
simplemente seguirá escuchando esas señales y actuará en consecuencia.
Código (TLDR)
Su Service
debe implementar un BroadcastReceiver
fin 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 Receiver
enService::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 Activity
debe 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
Service
y actualizar elActivity
. ¿Cómo verifico si elActivity
está en primer plano?
Por lo general, no es necesario verificar si Activity
está en primer plano o no. Simplemente envíe los datos a través LocalBroadcastManager
de su Service
. Si Activity
está activado, responderá y actuará.
Para esta situación muy común, el se Service
convierte en el remitente y el Activity
implementa el BroadcastReceiver
.
Entonces, crea un Receiver
en tu Activity
. Regístrese onResume()
y anule el registro onPause()
. No es necesario utilizar los otros métodos de ciclo de vida. .
Defina el Receiver
comportamiento 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 Activity
esté 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 Service
vía Intent
(consulte el código anterior) especificando la ID de la señal.
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
}
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 ActivityLifecycleCallbacks
eso como se ha recomendado en otras respuestas, he creado una pequeña clase de utilidad que alberga la lógica de si MyActivity
está 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
}
}
}
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í ...
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 ...
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.