Vérification de l’exécution d’une application Android en arrière-plan

Par arrière-plan, je veux dire qu’aucune des activités de l’application n’est actuellement visible par l’utilisateur?

Il existe peu de moyens de détecter si votre application s’exécute en arrière-plan, mais un seul d’entre eux est totalement fiable:

  1. La bonne solution (les crédits vont à Dan , CommonsWare et NeTeInStEiN )
    Suivez vous-même la visibilité de votre application à l’aide des méthodes Activity.onPause , Activity.onResume . Stocke le statut “visibilité” dans une autre classe. Les bons choix sont votre propre implémentation de l’ Application ou d’un Service (il existe également quelques variantes de cette solution si vous souhaitez vérifier la visibilité de l’activité depuis le service).

    Exemple
    Implémentez une classe d’ Application personnalisée (notez la méthode statique isActivityVisible() ):

     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; } 

    Enregistrez votre classe d’application dans AndroidManifest.xml :

      

    Ajoutez onPause et onResume à chaque Activity du projet (vous pouvez créer un ancêtre commun pour vos activités si vous le souhaitez, mais si votre activité est déjà étendue depuis MapActivity / ListActivity etc., vous devez toujours écrire ce qui suit à la main) :

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

    Mettre à jour
    ActivityLifecycleCallbacks a été ajouté au niveau 14 de l’API (Android 4.0). Vous pouvez les utiliser pour savoir si une activité de votre application est actuellement visible par l’utilisateur. Vérifiez la réponse ci-dessous pour les détails.

  2. Le mauvais
    J’avais l’habitude de suggérer la solution suivante:

    Vous pouvez détecter une application de premier plan / d’arrière-plan avec ActivityManager.getRunningAppProcesses() qui renvoie une liste d’enregistrements RunningAppProcessInfo . Pour déterminer si votre application est sur le premier plan, cochez la RunningAppProcessInfo.importance pour l’égalité avec RunningAppProcessInfo.IMPORTANCE_FOREGROUND pendant que RunningAppProcessInfo.processName est égal au nom de votre package d’application.

    En outre, si vous appelez ActivityManager.getRunningAppProcesses() partir du thread d’interface utilisateur de votre application, il renverra l’importance IMPORTANCE_FOREGROUND pour votre tâche, qu’elle soit ou non au premier plan. Appelez-le dans le thread d’arrière-plan (par exemple via AsyncTask ) et il retournera des résultats corrects.

    Bien que cette solution puisse fonctionner (et cela fonctionne effectivement la plupart du temps), je vous recommande fortement de ne pas l’utiliser. Et voici pourquoi. Comme Dianne Hackborn a écrit :

    Ces API ne sont pas là pour que les applications basent leur stream d’interface utilisateur, mais pour faire des choses comme montrer à l’utilisateur les applications en cours d’exécution, un gestionnaire de tâches, etc.

    Oui, il y a une liste gardée en mémoire pour ces choses. Cependant, il est désactivé dans un autre processus, géré par des threads s’exécutant séparément des vôtres, et pas quelque chose sur lequel vous pouvez (a) voir à temps pour prendre la bonne décision ou (b) avoir une image cohérente au moment de votre retour. De plus, la décision concernant la prochaine activité à effectuer se fait toujours au point où le changement doit avoir lieu, et ce n’est qu’à ce moment précis (où l’état de l’activité est brièvement verrouillé pour effectuer le changement) savoir réellement quelle sera la prochaine chose.

    Et la mise en œuvre et le comportement global ici ne sont pas garantis pour restr les mêmes à l’avenir.

    J’aurais aimé l’avoir lu avant de poster une réponse sur le SO, mais j’espère qu’il n’est pas trop tard pour admettre mon erreur.

  3. Une autre mauvaise solution
    La bibliothèque Droid-Fu mentionnée dans l’une des réponses utilise ActivityManager.getRunningTasks pour sa méthode isApplicationBroughtToBackground . Voir le commentaire de Dianne ci-dessus et n’utilisez pas cette méthode non plus.

La clé utilise ActivityLifecycleCallbacks (notez que cela nécessite le niveau d’API Android 14 (Android 4.0)). Vérifiez simplement si le nombre d’activités arrêtées est égal au nombre d’activités démarrées. Si elles sont égales, votre application est en cours de création. S’il y a plus d’activités démarrées, votre application est toujours visible. S’il y a plus d’activités que d’interruption, votre application est non seulement visible, mais elle est également au premier plan. Il y a 3 états principaux dans lesquels votre activité peut être, alors: visible et au premier plan, visible mais pas au premier plan, et non visible et pas au premier plan (c.-à-d. En arrière-plan).

Ce qui est vraiment intéressant avec cette méthode, c’est qu’elle n’a pas les problèmes asynchrones que getRunningTasks() , mais vous n’avez pas non plus besoin de modifier chaque Activity de votre application pour définir / désactiver quelque chose dans onResumed() / onPaused() . Ce ne sont que quelques lignes de code qui sont autonomes, et cela fonctionne dans toute votre application. De plus, il n’y a pas non plus de permissions géniales.

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 //  

@Mewzer a posé quelques bonnes questions sur cette méthode à laquelle je voudrais répondre dans cette réponse pour tous:

onStop() n'est pas appelé dans des situations de mémoire faible; est-ce un problème ici?

Les documents pour onStop() disent:

Notez que cette méthode peut ne jamais être appelée dans des situations de mémoire faible où le système ne dispose pas de suffisamment de mémoire pour que le processus de votre activité continue à s'exécuter après l'appel de la méthode onPause ().

La clé ici est de "maintenir le processus de votre activité en cours ..." Si cette situation de mémoire insuffisante est atteinte, votre processus est réellement tué (pas seulement votre activité). Cela signifie que cette méthode de vérification des arrière-plans est toujours valide car a) vous ne pouvez pas vérifier la mise en arrière de toute façon si votre processus est arrêté, et b) si votre processus redémarre (car une nouvelle activité est créée), le membre les variables (statiques ou non) pour MyLifecycleHandler seront réinitialisées à 0 .

Est-ce que cela fonctionne pour les changements de configuration?

Par défaut, non. Vous devez définir explicitement configChanges=orientation|screensize ( | avec tout ce que vous voulez) dans votre fichier manifeste et gérer les modifications de configuration, sinon votre activité sera détruite et recréée. Si vous ne le définissez pas, les méthodes de votre activité seront appelées dans cet ordre: onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume . Comme vous pouvez le voir, il n'y a pas de chevauchement (normalement, deux activités se chevauchent très brièvement lors de la commutation entre les deux, ce qui correspond au fonctionnement de cette méthode de détection d'arrière-plan). Pour contourner ce configChanges , vous devez définir configChanges afin que votre activité ne soit pas détruite. Heureusement, j'ai déjà dû définir des configChanges configuration dans tous mes projets car il était indésirable que toute mon activité soit détruite à l'écran par rotation / redimensionnement. Je n'ai donc jamais trouvé cela problématique. (merci à dpimka de m'avoir rafraîchi la mémoire et de me corriger!)

Une note:

Quand j'ai dit "background" ici dans cette réponse, j'ai voulu dire "votre application n'est plus visible". Les activités Android peuvent être visibles mais pas au premier plan (par exemple, s'il y a une superposition de notification transparente). C'est pourquoi j'ai mis à jour cette réponse pour refléter cela.

Il est important de savoir qu'Android a un moment d'inertie étrange lors du changement d'activités où rien n'est au premier plan . Pour cette raison, si vous vérifiez si votre application est en avant-plan lors du passage d’une activité à l’autre (dans la même application), vous serez informé que vous n’êtes pas au premier plan (même si votre application est toujours active et visible). ).

Vous pouvez vérifier si votre application est au premier plan dans la méthode super.onPause() votre Activity après super.onPause() . Rappelez-vous juste l'état étrange des limbes dont je viens de parler.

Vous pouvez vérifier si votre application est visible (c'est-à-dire si elle n'est pas en arrière-plan) dans la méthode super.onStop() votre Activity après super.onStop() .

La réponse d’Idolon est sujette aux erreurs et bien plus compliquée bien que repeter ici vérifier que l’application Android est au premier plan ou non? et ici Déterminer l’application actuelle au premier plan à partir d’une tâche ou d’un service d’arrière-plan

Il y a une approche beaucoup plus simple:

Sur une BaseActivity étendue à toutes les activités:

 protected static boolean isVisible = false; @Override public void onResume() { super.onResume(); setVisible(true); } @Override public void onPause() { super.onPause(); setVisible(false); } 

Chaque fois que vous devez vérifier si l’une de vos activités d’application est au premier plan, cochez simplement la isVisible() ;

Pour comprendre cette approche, consultez la réponse du cycle de vie des activités côte à côte : Cycle de vie des activités côte à côte

GOOGLE SOLUTION – pas un hack, comme les solutions précédentes. Utiliser ProcessLifecycleOwner

 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 } } 

dans app.gradle

 dependencies { ... implementation "android.arch.lifecycle:extensions:1.1.0" } allprojects { repositories { ... google() jcenter() maven { url 'https://maven.google.com' } } } 

Depuis Android API 16, il existe un moyen simple de vérifier si l’application est au premier plan. Il n’est peut-être pas infaillible, mais aucune méthode sur Android n’est infaillible. Cette méthode est assez bonne pour être utilisée lorsque votre service reçoit des mises à jour du serveur et doit décider d’afficher une notification ou non (car si l’interface utilisateur est au premier plan, l’utilisateur remarquera la mise à jour sans notification).

 RunningAppProcessInfo myProcess = new RunningAppProcessInfo(); ActivityManager.getMyMemoryState(myProcess); isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND; 

J’ai essayé la solution recommandée qui utilise Application.ActivityLifecycleCallbacks et bien d’autres, mais ils ne fonctionnaient pas comme prévu. Grâce à Sarge , j’ai trouvé une solution simple et directe que je décris ci-dessous.

La clé de la solution est le fait de comprendre que si nous avons ActivityA et ActivityB et que nous appelons ActivityB à partir d’ActivityA (sans appeler ActivityA.finish ), alors le ActivitySt onStart() sera appelé avant ActivityA onStop() .

C’est aussi la principale différence entre onStop() et onPause() que personne n’a mentionné dans les articles que j’ai lus.

Donc, en fonction du comportement du cycle de vie de cette activité, vous pouvez simplement compter combien de fois onStart() et onPause() été appelés dans votre programme. Notez que pour chaque Activity de votre programme, vous devez remplacer onStart() et onStop() , afin d’incrémenter / décrémenter la variable statique utilisée pour le comptage. Voici le code implémentant cette logique. Notez que j’utilise une classe qui étend l’ Application , alors n’oubliez pas de déclarer sur Manifest.xml intérieur de la balise Application: android:name=".Utilities" , même si elle peut être implémentée avec une simple classe personnalisée.

 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--; } } 

Maintenant, pour chaque activité de notre programme, nous devons remplacer onStart() et onStop() et incrémenter / décrémenter comme indiqué ci-dessous:

 @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(); } 

Avec cette logique, il y a 2 cas possibles:

  1. stateCounter = 0 : le nombre d’ stateCounter = 0 est égal au nombre d’activités démarrées, ce qui signifie que l’application s’exécute en arrière-plan.
  2. stateCounter > 0 : le nombre de démarrés est supérieur au nombre d’arrêts, ce qui signifie que l’application s’exécute au premier plan.

Remarque: stateCounter < 0 signifierait qu'il y a plus d'activités arrêtées plutôt que démarrées, ce qui est impossible. Si vous rencontrez ce cas, cela signifie que vous n'augmentez / diminuez pas le compteur comme vous le devriez.

Vous êtes prêt à partir. Vous devriez vouloir vérifier si votre application est en arrière-plan dans onStop() .

Il n’y a aucun moyen, à part de le suivre vous-même, pour déterminer si l’une de vos activités est visible ou non. Vous devriez peut-être envisager de poser une nouvelle question sur StackOverflow, en expliquant ce que vous essayez d’atteindre à partir d’une expérience utilisateur, afin que nous puissions peut-être vous donner des idées de mise en œuvre alternatives.

Vous pouvez utiliser ComponentCallbacks2 pour détecter si l’application est en arrière-plan. BTW ce rappel est uniquement disponible dans API Level 14 (Ice Cream Sandwich) et au-dessus.

Vous recevrez un appel à la méthode:

public abstract void onTrimMemory (int level)

si le niveau est ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN alors l’application est en arrière-plan.

Vous pouvez implémenter cette interface pour une activity , un 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 } } } 

En vous appuyant sur @Cornstalks, vous trouverez quelques fonctionnalités utiles.

Fonctionnalités supplémentaires:

  • a introduit le modèle singleton de sorte que vous pouvez le faire n’importe où dans l’application: AppLifecycleHandler.isApplicationVisible () et AppLifecycleHandler.isApplicationInForeground ()
  • ajout de la gestion des événements en double (voir les commentaires // agir sur le changement de visibilité et // agir sur le changement de premier plan)

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 Ssortingng 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; } } 

Si vous activez les parameters du développeur “Ne pas conserver les activités” – cochez uniquement le nombre d’activités créées ne suffit pas. Vous devez également vérifier isSaveInstanceState . Ma méthode personnalisée est la vérification isApplicationRunning () est l’application Android est en cours d’exécution:

Voici mon code de travail:

 public class AppLifecycleService implements Application.ActivityLifecycleCallbacks { private int created; private boolean isSaveInstanceState; private static AppLifecycleService instance; private final static Ssortingng 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) { } } 

Pour utiliser ce que CommonsWare et Key ont dit, vous pouvez peut-être étendre la classe Application et demander à toutes vos activités d’appeler cela sur leurs méthodes onPause / onResume. Cela vous permettrait de savoir quelles activités sont visibles, mais cela pourrait probablement être mieux géré.

Pouvez-vous préciser ce que vous avez en tête exactement? Lorsque vous dites que courir en arrière-plan, cela signifie-t-il simplement que votre application est encore en mémoire même si elle n’est pas actuellement à l’écran? Avez-vous envisagé d’utiliser les services comme un moyen plus persistant de gérer votre application lorsqu’elle n’est pas au point?

J’ai effectué ma propre implémentation de ActivityLifecycleCallbacks. J’utilise SherlockActivity, mais pour l’activité normale, la classe peut fonctionner.

Premièrement, je crée une interface qui a toutes les méthodes pour suivre le cycle de vie des activités:

 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); } 

Deuxièmement, j’ai implémenté cette interface dans la classe de mon application:

 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()); } } 

Troisièmement, je crée une classe qui s’étend de 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); } } 

Quasortingèmement, toutes les classes qui s’étendent depuis SherlockActivity, j’ai remplacé MySherlockActivity:

 public class MainActivity extends MySherlockActivity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } } 

Maintenant, dans le logcat, vous verrez les journaux programmés dans l’implémentation de l’interface faite dans MyApplication.

La meilleure solution que j’ai trouvée utilise des timers.

Vous avez démarré un minuteur dans onPause () et annulé le même minuteur dans onResume (), il y a 1 instance du minuteur (généralement définie dans la classe Application). Le minuteur lui-même est configuré pour exécuter un Runnable après 2 secondes (ou tout autre intervalle que vous jugez approprié), lorsque le minuteur se déclenche, vous définissez un indicateur marquant l’application comme étant en arrière-plan.

Dans la méthode onResume () avant d’annuler le minuteur, vous pouvez interroger l’indicateur d’arrière-plan pour effectuer toute opération de démarrage (par exemple, lancer des téléchargements ou activer les services de localisation).

Cette solution vous permet d’avoir plusieurs activités sur la stack arrière et ne nécessite aucune autorisation pour implémenter.

Cette solution fonctionne bien si vous utilisez également un bus d’événement, car votre timer peut simplement déclencher un événement et diverses parties de votre application peuvent réagir en conséquence.

Activity gets paused when a Dialog comes above it so all the recommended solutions are half-solutions. You need to create hooks for dialogs as well.

Since it isn’t already mentioned, I will suggest the readers to explore ProcessLifecycleOwner available through Android Architecture components

Offical docs:

The system distinguishes between foreground and background apps. (The definition of background for purposes of service limitations is distinct from the definition used by memory management; an app might be in the background as pertains to memory management , but in the foreground as pertains to its ability to launch services.) An app is considered to be in the foreground if any of the following is true:

  1. It has a visible activity, whether the activity is started or paused.
  2. It has a foreground service.
  3. Another foreground app is connected to the app, either by binding to one of its services or by making use of one of its content providers. For example, the app is in the foreground if another app binds to its:
    • IME
    • Wallpaper service
    • Notification listener
    • Voice or text service

If none of those conditions is true, the app is considered to be in the background.

In my activities onResume and onPause I write an isVisible boolean to SharedPrefences.

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

And read it elsewhere when needed via,

  // 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)){...} 

Maybe not elegant, but it works for me…

Another solution for this old post (for those that it might help) :


  

 public class BaseApplication extends Application { private class Status { public boolean isVisible = true; public boolean isFocused = true; } private Map activities; @Override public void onCreate() { activities = new HashMap(); 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(); } } 

See the comment in the onActivityDestroyed function.

Works with SDK target version 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++; } } 

You should use a shared preference to store the property and act upon it using service binding from your activities. If you use binding only, (that is never use startService), then your service would run only when you bind to it, (bind onResume and unbind onPause) that would make it run on foreground only, and if you do want to work on background you can use the regular start stop service.

I think this question should be more clear. Quand? Où? What is your specific situation you want to konw if your app is in background?

I just introduce my solution in my way.
I get this done by using the field “importance” of RunningAppProcessInfo class in every activity’s onStop method in my app, which can be simply achieved by providing a BaseActivity for other activities to extend which implements the onStop method to check the value of “importance”. Voici le code:

 public static boolean isAppRunning(Context context) { ActivityManager activityManager = (ActivityManager) context .getSystemService(Context.ACTIVITY_SERVICE); List appProcesses = activityManager .getRunningAppProcesses(); for (RunningAppProcessInfo appProcess : appProcesses) { if (appProcess.processName.equals(context.getPackageName())) { if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) { return true; } } } return false; } 

I recommend reading through this page: http://developer.android.com/reference/android/app/Activity.html

In short, your activity is no longer visible after onStop() has been called.

What about using getApplicationState().isInForeground() ?

In my opinion, many answers introduce a heavy load of code and bring lots of complexity and non-readability.

When people ask on SO how to communicate between a Service and a Activity , I usually advice to use the LocalBroadcastManager .


Pourquoi?

Well, by quoting the docs:

  • You know that the data you are broadcasting won’t leave your app, so don’t need to worry about leaking private data.

  • It is not possible for other applications to send these broadcasts to your app, so you don’t need to worry about having security holes they can exploit.

  • It is more efficient than sending a global broadcast through the system.

Not in the the docs:

  • It does not require external libraries
  • The code is minimal
  • It’s fast to implement and understand
  • No custom self-implemented callbacks / ultra-singleton / intra-process pattern whatsoever…
  • No strong references on Activity , Application , …

La description

So, you want to check if any of the Activity is currently in the foreground. You usually do that in a Service , or your Application class.

This means, your Activity objects become the sender of a signal (I’m on / I’m off). Your Service , on the other hand, becomes the Receiver .

There are two moments in which your Activity tells you if it’s going in the foreground or in the background (yes only two… not 6).

When the Activity goes into the foreground, the onResume() method is sortingggered (also called after onCreate() ).

When the Activity goes in the back, onPause() is called.

These are the moments in which your Activity should send the signal to your Service to describe its state.

In case of multiple Activity ‘s, remember the an Activity goes into the background first, then another one comes into the foreground.

So the situation would be:*

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

The Service / Application will simply keep listening for those signals and act accordingly.


Code (TLDR)

Your Service must implement a BroadcastReceiver in order to listen for signals.

 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") 

Register the Receiver in Service::onCreate()

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

Un-register it in Service::onDestroy()

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

Now your Activity ‘s must communicated their state.

In Activity::onResume()

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

In Activity::onPause()

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

A very, very common situation

Developer: I want to send data from my Service and update the Activity . How do I check if the Activity is in the foreground?

There is usually no need to check if the Activity is in the foreground or not. Just send the data via LocalBroadcastManager from your Service . If the Activity is on, then it will respond and act.

For this very common situation, the Service becomes the sender, and the Activity implements the BroadcastReceiver .

So, create a Receiver in your Activity . Register it in onResume() and un-register it in onPause() . There is no need to use the other life-cycle methods .

Define the Receiver behavior in onReceive() (update ListView, do this, do that, …).

This way the Activity will listen only if it’s in the foreground and nothing will happen if it’s in the back or is destroyed.

In case of multiple Activity ‘s, whichever Activity is on will respond (if they also implement the Receiver ).

If all are in the background, nobody will respond and the signal will simply get lost.

Send the data from the Service via Intent (see code above) by specifying the signal ID.


  • Except for Multi-Window Support . It may be sortingcky (please test it if needed)…

It might be too late to answer but if somebody comes visiting then here is the solution I suggest, The reason(s) an app wants to know it’s state of being in background or coming to foreground can be many, a few are, 1. To show toasts and notifications when the user is in BG. 2.To perform some tasks for the first time user comes from BG, like a poll, redraw etc.

The solution by Idolon and others takes care of the first part, but does not for the second. If there are multiple activities in your app, and the user is switching between them, then by the time you are in second activity, the visible flag will be false. So it cannot be used deterministically.

I did something what was suggested by CommonsWare, “If the Service determines that there are no activities visible, and it remains that way for some amount of time , stop the data transfer at the next logical stopping point.”

The line in bold is important and this can be used to achieve second item. So what I do is once I get the onActivityPaused() , don not change the visible to false directly, instead have a timer of 3 seconds (that is the max that the next activity should be launched), and if there is not onActivityResumed() call in the next 3 seconds, change visible to false. Similarly in onActivityResumed() if there is a timer then I cancel it. To sum up,the visible becomes isAppInBackground.

Sorry cannot copy-paste the code…

Another simple and accurate solution. Complete gist here

 public class BaseLifeCycleCallbacks implements Application.ActivityLifecycleCallbacks { HashMap activities; BaseLifeCycleCallbacks() { activities = new HashMap(); } @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { } @Override public void onActivityStarted(Activity activity) { //map Activity unique class name with 1 on foreground activities.put(activity.getLocalClassName(), 1); applicationStatus(); } @Override public void onActivityResumed(Activity activity) { } @Override public void onActivityPaused(Activity activity) { } @Override public void onActivityStopped(Activity activity) { //map Activity unique class name with 0 on foreground activities.put(activity.getLocalClassName(), 0); applicationStatus(); } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } @Override public void onActivityDestroyed(Activity activity) { } /** * Check if any activity is in the foreground */ private boolean isBackGround() { for (Ssortingng s : activities.keySet()) { if (activities.get(s) == 1) { return false; } } return true; } /** * Log application status. */ private void applicationStatus() { Log.d("ApplicationStatus", "Is application background" + isBackGround()); if (isBackGround()) { //Do something if the application is in background } } 

I would like to recommend you to use another way to do this.

I guess you want to show start up screen while the program is starting, if it is already running in backend, don’t show it.

Your application can continuously write current time to a specific file. While your application is starting, check the last timestamp, if current_time-last_time>the time range your specified for writing the latest time, it means your application is stopped, either killed by system or user himself.