374 votes

Vérifier si une application Android est exécutée 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 ?

3 votes

11 votes

Je suis confus ici pourquoi Android ne peut pas fournir une simple surcharge de la classe d'application pour cela ? Est-ce trop difficile de savoir cela au niveau de la plateforme ? @Override protected void onApplicationSentToBackground() { }

2 votes

@ChuckD - ce serait logique, et c'est ce que le SDK Android semble éviter de faire parfois :/.

399voto

Idolon Points 11503

Il existe quelques moyens de détecter si votre application fonctionne en arrière-plan, mais un seul d'entre eux est totalement fiable :

  1. La bonne solution (les crédits vont à Dan , CommonsWare y NeTeInStEiN )
    Suivez vous-même la visibilité de votre application en utilisant Activity.onPause , Activity.onResume méthodes. Stocker le statut de "visibilité" dans une autre classe. Les bons choix sont votre propre implémentation de la méthode Application ou un Service (il y a aussi quelques variations de cette solution si vous souhaitez vérifier la visibilité de l'activité du service).
     
    Exemple
    Mettre en œuvre des solutions personnalisées Application (notez le isActivityVisible() méthode statique) :

    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 :

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

    Ajouter onPause y onResume à chaque Activity dans le 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 à partir de 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();
    }

     
    Mise à jour
    ActivityLifecycleCallbacks ont été ajoutés 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érifier Réponse de Cornstalks ci-dessous pour les détails.

  2. Le mauvais
    J'avais l'habitude de proposer la solution suivante :

    Vous pouvez détecter l'application actuellement au premier plan/arrière-plan avec ActivityManager.getRunningAppProcesses() qui renvoie une liste de RunningAppProcessInfo dossiers. Pour déterminer si votre application est au premier plan, vérifiez RunningAppProcessInfo.importance pour l'égalité avec RunningAppProcessInfo.IMPORTANCE_FOREGROUND tandis que RunningAppProcessInfo.processName est égal au nom du paquetage de votre application.

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

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

    Ces API ne sont pas là pour que les applications basent leur flux d'interface utilisateur sur elles, mais pour faire des choses comme montrer à l'utilisateur les applications en cours, ou un gestionnaire de tâches, ou autre.

    Oui, il y a une liste gardée en mémoire pour ces choses. Cependant, elle se trouve dans un autre processus, gérée par des threads distincts du vôtre, et ce n'est pas quelque chose que vous pouvez espérer (a) voir à temps pour prendre la bonne décision ou (b) avoir une image cohérente au moment où vous revenez. De plus, la décision concernant l'activité "suivante" est toujours prise au moment où le changement doit avoir lieu, et ce n'est qu'à ce moment précis (lorsque l'état de l'activité est brièvement verrouillé pour effectuer le changement) que nous savons avec certitude quelle sera la prochaine activité.

    Et il n'est pas garanti que la mise en œuvre et le comportement global ici restent les mêmes à l'avenir.

    J'aurais aimé lire ceci avant de poster une réponse sur l'OS, mais j'espère qu'il n'est pas trop tard pour admettre mon erreur.

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

4 votes

Pour savoir si vous avez appuyé sur le bouton d'accueil ou si une autre application a pris le focus : 1) mettez en œuvre la fonction bonne solution . 2) Dans OnStop demande à isActivityVisible .

30 votes

Malheureusement, votre solution "correcte" ne fonctionne pas pour moi. Considérons que vous effectuez un cycle d'activités dans votre application. Ce qui se passe alors, c'est que votre drapeau 'inForeground' se comporte comme suit : Vrai, Faux (entre le onPause de la 1ère activité et le onResume de la 2ème activité) puis Vrai à nouveau, etc. Vous auriez alors besoin d'une sorte d'hystérésis.

16 votes

Cette solution ne fonctionne pas si vous ne pouvez pas contrôler directement toutes les activités. Par exemple, si vous avez une activité provenant d'un sdk tiers ou même si vous lancez une intention ACTION_VIEW.

268voto

Cornstalks Points 9261

NE PAS UTILISER CETTE RÉPONSE

La réponse de l'utilisateur1269737 est la façon correcte (approuvée par Google/Android) de procéder. . Allez lire leur réponse et donnez-leur un +1.

Je vais laisser ma réponse originale ici pour la postérité. C'était la meilleure disponible en 2012, mais maintenant Android a un support approprié pour cela.

Réponse originale

La clé est d'utiliser ActivityLifecycleCallbacks (notez que cela nécessite le niveau 14 de l'API Android (Android 4.0)). Vérifiez simplement si le nombre d'activités arrêtées est égal au nombre d'activités lancées. S'ils sont égaux, votre application est en arrière-plan. S'il y a plus d'activités démarrées, votre application est toujours visible. S'il y a plus d'activités reprises que d'activités arrêtées, votre application est non seulement visible, mais elle est aussi au premier plan. Votre activité peut donc se trouver dans trois états principaux : visible et au premier plan, visible mais pas au premier plan, et non visible et pas au premier plan (c'est-à-dire en arrière-plan).

Ce qui est vraiment bien avec cette méthode, c'est qu'il n'y a pas de problèmes d'asynchronisme. getRunningTasks() mais il n'est pas non plus nécessaire de modifier chaque Activity dans votre application afin d'activer/désactiver quelque chose dans l'application onResumed() / onPaused() . Il ne s'agit que de quelques lignes de code autonomes, qui fonctionnent dans l'ensemble de votre application. De plus, il n'est pas nécessaire d'obtenir des autorisations bizarres.

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

MonApplication.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 a posé de bonnes questions sur cette méthode, auxquelles j'aimerais répondre dans cette réponse pour tout le monde :

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

Non. Les docs pour onStop() dites :

Notez que cette méthode peut ne jamais être appelée, dans les situations de faible mémoire où le système n'a pas assez de mémoire pour maintenir le processus de votre activité en cours d'exécution après l'appel de sa méthode onPause().

La clé ici est de "garder votre activité processus en cours ..." Si cette situation de mémoire faible est atteinte, votre processus est tué (pas seulement votre activité). Cela signifie que cette méthode de vérification de l'arrière-plan est toujours valable car a) vous ne pouvez pas vérifier l'arrière-plan de toute façon si votre processus est tué, et b) si votre processus redémarre (parce qu'une nouvelle activité est créée), les variables membres (qu'elles soient statiques ou non) de l'élément MyLifecycleHandler sera réinitialisé à 0 .

Cela fonctionne-t-il pour les changements de configuration ?

Par défaut, non. Vous devez explicitement définir configChanges=orientation|screensize ( | avec tout ce que vous voulez) dans votre fichier manifeste et gérer les changements de configuration, sinon votre activité sera détruite et recréée. Si vous ne définissez pas cette option, 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 constater, il n'y a pas de chevauchement (normalement, deux activités se chevauchent très brièvement lorsqu'on passe de l'une à l'autre, c'est ainsi que fonctionne cette méthode de détection de l'arrière-plan). Afin de contourner ce problème, vous devez définir configChanges afin que votre activité ne soit pas détruite. Heureusement, je n'ai pas eu à mettre configChanges déjà dans tous mes projets parce qu'il n'était pas souhaitable que toute mon activité soit détruite par la rotation/le redimensionnement de l'écran, donc je n'ai jamais trouvé que cela posait problème. (merci à dpimka de m'avoir rafraîchi la mémoire sur ce point et de m'avoir corrigé !)

Une note :

Lorsque j'ai dit "arrière-plan" dans cette réponse, je voulais dire "votre application n'est plus visible". Les activités Android peuvent être visibles mais ne pas être au premier plan (par exemple, s'il y a une superposition de notifications 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 de limbo bizarre quand on change d'activité où rien n'est au premier plan . Pour cette raison, si vous vérifiez si votre application est au premier plan lorsque vous passez d'une activité à l'autre (dans la même application), on vous dira que vous n'êtes pas au premier plan (même si votre application est toujours l'application active et est visible).

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

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

1 votes

Cela semble intéressant, mais que se passe-t-il en cas de manque de mémoire ? Il n'est pas garanti que la fonction onStop() soit appelée. Pourrions-nous un jour nous retrouver dans la situation où un onStop() n'est pas appelé et où le compteur d'arrêts n'est pas incrémenté - ce qui signifie que la vérification en arrière-plan n'est plus fiable ? Ou est-ce que cela n'arrivera jamais ?

1 votes

En outre, cela ignorera-t-il les changements de configuration ? Ou l'application sera-t-elle considérée comme étant en arrière-plan si une activité est recréée à la suite d'un changement de configuration (par exemple, un changement d'orientation) ? Désolé pour les questions, mais je pense que vous êtes sur la bonne voie et je suis intéressé de savoir si cela fonctionne dans ces cas limites.

1 votes

@Mewzer : J'allais répondre en tant que commentaire, mais il va falloir taper un peu pour obtenir ces réponses, alors revenez dans quelques minutes et je modifierai ma réponse.

18voto

NeTeInStEiN Points 7331

La réponse d'Idolon est sujette à des erreurs et beaucoup plus compliquée, bien que répétée ici. vérifier si l'application Android est au premier plan ou non ? et ici Détermination de l'application de premier plan actuelle à partir d'une tâche ou d'un service d'arrière-plan

Il existe une approche beaucoup plus simple :

Sur un BaseActivity que toutes les activités étendent :

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 avez besoin de vérifier si l'une des activités de votre application est au premier plan, vérifiez simplement isVisible() ;

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

3 votes

Idolon's answer is error prone - malheureusement je dois être d'accord avec vous. Suite au commentaire de Dianne Hackborn dans le groupe Google, j'ai mis à jour ma réponse. Consultez-la pour en connaître les détails.

2 votes

C'est no une solution infaillible non plus. Un scénario possible est que si l'utilisateur tire le panneau de notification vers le bas, alors ni l'option onPause , onStop ni le onResume est appelé. Que faire alors si aucun de ces événements n'est déclenché !

0 votes

Cela m'a conduit à cette question : stackoverflow.com/questions/33657102/

10voto

CommonsWare Points 402670

Il n'y a aucun moyen, à moins que vous ne le suiviez vous-même, de déterminer si l'une de vos activités est visible ou non. Peut-être devriez-vous envisager de poser une nouvelle question sur StackOverflow, en expliquant ce que vous essayez d'obtenir d'une expérience utilisateur, afin que nous puissions peut-être vous donner d'autres idées de mise en œuvre.

2 votes

Dans Android, nous avons un paramètre appelé "Background Data". Ce paramètre permet de désactiver toute connexion aux données d'arrière-plan lorsque l'application fonctionne en arrière-plan. Je veux mettre en œuvre le paramètre "Background data" pour mon application, de sorte que lorsqu'aucune de mes activités n'est visible pour l'utilisateur, je voudrais que mon service cesse de transférer des données, mais dès qu'une de mes activités reprend, je voudrais reprendre le transfert de données.

1 votes

@cppdev : Avec un peu de chance, le "transfert de données" est effectué par un Service . Si c'est le cas, faites en sorte que vos activités notifient le service au fur et à mesure de leur apparition et de leur disparition. Si le Service détermine qu'aucune activité n'est visible et qu'il en est ainsi pendant un certain temps, arrêtez le transfert de données au prochain point d'arrêt logique. Oui, cela nécessitera du code pour chacune de vos activités, mais pour l'instant, c'est inévitable AFAIK.

1 votes

Si vous voulez éviter de copier-coller le code commun entre toutes vos activités, vous pouvez créer une classe MyActivityClass héritant de Activity et l'implémentation des méthodes du cycle de vie, et faites en sorte que toutes vos activités héritent de MyActivityClass . Cela ne fonctionnera pas pour PreferenceActivity o MapActivity cependant (voir cette question )

1voto

Key Points 3331

Je vous recommande de lire cette page : http://developer.Android.com/reference/Android/app/Activity.html

En bref, votre activité n'est plus visible après l'entrée en vigueur de la loi. onStop() a été appelé.

3 votes

J'ai environ 10 activités dans mon application. Je veux donc savoir si aucune d'entre elles n'est visible pour l'utilisateur. En somme, je veux savoir si l'ensemble de mon application fonctionne en arrière-plan.

0 votes

Donc, vous gardez une trace de tous les 10 et quelques. Ou, comme CommonsWare l'a suggéré, expliquez ce que vous essayez de faire.

3 votes

Ce n'est pas correct. Votre activité est visible jusqu'à onStop ; entre onPause y onStop c'est visible mais pas au premier plan .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X