Vérifier si une application Android s'exécute en arrière-plan

329

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

cppdev
la source
7
Je suis confus ici .. pourquoi Android ne peut-il pas fournir un simple remplacement de la classe d'application pour cela? Est-il trop difficile de le savoir au niveau de la plateforme? @Override protected void onApplicationSentToBackground () {}
Chuck D
2
@ChuckD - cela aurait du sens, ce que le SDK Android semble éviter parfois de faire. : /
Mark
1
iOS a cela à la pelle, je ne sais pas pourquoi Google rend cela si difficile. C'est un besoin tellement évident.
Jerry Destremps

Réponses:

388

Il existe plusieurs façons de détecter si votre application s'exécute en arrière-plan, mais une seule d'entre elles est totalement fiable:

  1. La bonne solution (crédits vont à Dan , CommonsWare et NeTeInStEiN )
    visibilité piste de votre application par vous - même à l' aide Activity.onPause, des Activity.onResumeméthodes. Stockez le statut de "visibilité" dans une autre classe. Les bons choix sont votre propre implémentation du Applicationou du Service(il existe également quelques variantes de cette solution si vous souhaitez vérifier la visibilité des activités depuis le service).
     
    Exemple d'
    implémentation personnaliséeApplication classe (notez la 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" >

    Ajoutez onPauseet onResumeà chacun Activitydans 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/ ListActivityetc., 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();
    }

     
    La mise à jour
    ActivityLifecycleCallbacks a été ajoutée au niveau 14 de l'API (Android 4.0). Vous pouvez les utiliser pour suivre si une activité de votre application est actuellement visible par l'utilisateur. Vérifier la réponse de Cornstalks ci-dessous pour plus de détails.

  2. Le mauvais
    j'ai utilisée pour suggérer la solution suivante:

    Vous pouvez détecter l'application actuellement au premier plan / en arrière-plan avec ActivityManager.getRunningAppProcesses()laquelle renvoie une liste d' RunningAppProcessInfoenregistrements. Pour déterminer si votre application est au premier plan, vérifiezRunningAppProcessInfo.importance que l'égalité à RunningAppProcessInfo.IMPORTANCE_FOREGROUNDwhile RunningAppProcessInfo.processNameest égale au nom de votre package d'application.

    De plus, si vous appelez à ActivityManager.getRunningAppProcesses()partir de votre thread d'interface utilisateur d'application, cela reviendra à l'importance IMPORTANCE_FOREGROUNDde votre tâche, qu'elle soit réellement au premier plan ou non. 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 elle fonctionne en fait la plupart du temps), je recommande fortement 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, mais pour faire des choses comme montrer à l'utilisateur les applications en cours d'exécution, ou un gestionnaire de tâches, ou autre.

    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 fonctionnant séparément du vôtre, et pas quelque chose sur lequel vous pouvez compter (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 destination de la "prochaine" activité est toujours prise au point où le changement doit se produire, et ce n'est qu'à ce moment précis (où l'état d'activité est brièvement verrouillé pour effectuer le changement) que nous en fait savoir avec certitude quelle sera la prochaine chose.

    Et la mise en œuvre et le comportement global ici ne sont pas garantis de rester les mêmes à l'avenir.

    J'aimerais avoir lu ceci 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 que la bibliothèque
    Droid-Fu mentionnée dans l'une des réponses utilise ActivityManager.getRunningTaskspour sa isApplicationBroughtToBackgroundméthode. Voir le commentaire de Dianne ci-dessus et n'utilisez pas non plus cette méthode.

Idolon
la source
4
Pour savoir si vous avez appuyé sur le bouton d'accueil ou une autre application a gagné le focus: 1) implémentez la bonne solution . 2) À la OnStopdemande de isActivityVisible.
Brais Gabin
28
Malheureusement, votre «bonne» solution ne fonctionne pas pour moi. Pensez à parcourir les activités de votre application. Ce qui se passe alors, c'est que votre indicateur 'inForeground' se présente comme suit: Vrai, Faux (entre la 1ère activité de la pause et la 2e activité de la reprise) puis à nouveau vrai, etc. Vous auriez alors besoin d'une hystérésis d'une certaine sorte.
Radu
14
Cette solution ne fonctionne pas si vous ne pouvez pas contrôler directement toutes les activités. Par exemple, si vous avez une activité à partir d'un SDK tiers ou même lancez une intention ACTION_VIEW.
user123321
66
Android est une telle épave. Personne ne pensait que quelqu'un pourrait vouloir conserver les données au niveau de l'application? Donnez-moi une pause
8
Il semble que la vraie réponse à cette question soit "Vous ne pouvez pas le vérifier correctement". La solution dite «correcte» est au mieux une solution de contournement, tout comme l'utilisation d'ActivityLifecycleCallbacks. Vous devez toujours envisager de basculer entre les activités qui seraient enregistrées comme "non au premier plan". Ça me souffle que vous ne pouvez pas vérifier une chose simple comme ça ...
serine
263

N'UTILISEZ PAS CETTE RÉPONSE

La réponse de user1269737 est la bonne façon (approuvée par Google / Android) de le faire . Allez lire leur réponse et donnez-leur un +1.

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

Réponse originale

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. 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 suspendues, 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'est-à-dire en arrière-plan).

La chose vraiment agréable à propos de cette méthode est qu'elle n'a pas les problèmes asynchrones getRunningTasks(), mais vous n'avez pas non plus à modifier chaque Activityélément de votre application pour définir / supprimer 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, aucune autorisation funky n'est requise non plus.

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 a posé quelques 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 mémoire insuffisante; est-ce un problème ici?

Non. Les documents pour onStop()dire:

Notez que cette méthode peut ne jamais être appelée, dans des 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 "garder le processus de votre activité en cours d'exécution ..." Si cette situation de mémoire insuffisante est jamais atteinte, votre processus est réellement tué (pas seulement votre activité). Cela signifie que cette méthode de vérification de l'arrière-plan est toujours valide, car a) vous ne pouvez pas de toute façon vérifier l'arrière-plan si votre processus est interrompu, et b) si votre processus redémarre (car une nouvelle activité est créée), le membre les variables (statiques ou non) de MyLifecycleHandlerseront 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 définissez pas, seront appelés les méthodes de votre activité 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 du basculement entre les deux, c'est ainsi que cette méthode de détection d'arrière-plan fonctionne). Pour contourner ce problème, vous devez vous préparer de configChangesmanière à ce que votre activité ne soit pas détruite. Heureusement, j'ai dû réglerconfigChangesdéjà dans tous mes projets car il n'était pas souhaitable que toute mon activité soit détruite à l'écran en rotation / redimensionnement, donc je n'ai jamais trouvé cela problématique. (merci à dpimka d'avoir rafraîchi ma mémoire à ce sujet et de m'avoir corrigé!)

Une note:

Lorsque j'ai dit «arrière-plan» ici dans cette réponse, je voulais 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 de limbes é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 au premier plan lors du basculement entre les activités (dans la même application), vous serez informé que vous n'êtes pas au premier plan (même si votre application est toujours l'application active et qu'elle est visible). ).

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

Vous pouvez vérifier si votre application est visible (si ce n'est pas en arrière - plan) dans votre Activityde » onStop()méthode après super.onStop() .

Cornstalks
la source
1
Cela semble intéressant - mais que se passe-t-il dans les situations de mémoire insuffisante? Il n'est pas garanti que onStop () sera appelé. Pourrions-nous jamais entrer dans la situation où un onStop () n'est pas appelé et le compteur arrêté n'est pas incrémenté - cela signifie que la vérification en arrière-plan n'est plus fiable? Ou cela n'arriverait-il jamais?
Mewzer
1
Cela ignorera-t-il également les modifications de configuration? Ou l'application sera-t-elle considérée comme mise en arrière-plan si une activité est recréée à la suite d'un changement de configuration (par exemple, changement d'orientation)?. Désolé, pour les questions, mais je pense que vous êtes sur quelque chose et que vous souhaitez savoir si cela fonctionne dans ces cas de bord.
Mewzer
1
@Mewzer: J'allais répondre en tant que commentaire, mais cela va prendre un peu de frappe pour obtenir ces réponses, alors revenez dans quelques minutes et je modifierai ma réponse.
Cornstalks
1
@Mewzer: Vous devriez trouver vos réponses maintenant. Faites-moi savoir s'il y a d'autres questions!
Cornstalks
2
@Mewzer: Je viens d'ajouter une note qui pourrait vous intéresser. Plus précisément, vérifiez les antécédents onStop()après super.onStop(). Ne vérifiez pas l'arrière-plan onPause().
Cornstalks
187

SOLUTION GOOGLE - pas un hack, comme les solutions précédentes. Utilisez 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
    }
}

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

Vous pouvez en savoir plus sur les composants d'architecture liés au cycle de vie ici - https://developer.android.com/topic/libraries/architecture/lifecycle

user1269737
la source
10
Cela devrait certainement être la bonne réponse! Cela a fonctionné comme un charme: D
JaviOverflow
2
Cela fonctionne parfaitement, j'ai également modifié un peu afin de pouvoir accéder plus facilement à l'état de premier plan / arrière-plan en dehors de cette classe: companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }alors vous pouvez obtenir l'état de premier plan avecArchLifecycleApp.isForeground()
Jose Jet
2
Oh mec, c'est tellement mieux que mon ancienne réponse. Ayez un +1 de moi. J'ai mis à jour ma réponse pour pointer les gens vers la vôtre.
Cornstalks
2
Bien qu'il s'agisse d'une bonne réponse, il n'est pas nécessaire d'implémenter des rappels, vous pouvez simplement interroger ProcessLifecycleOwner quand vous le souhaitez. Vérifiez stackoverflow.com/a/52678290/6600000
Keivan Esbati
2
Comme le dit le doc The LifecycleOwner for the whole application process. Note that if your application has multiple processes, this provider does not know about other processes. , cela ne fonctionne pas pour les multiple processesapplications, existe-t-il des API que nous pouvons réaliser avec élégance?
acntwww
23

À partir de la bibliothèque de support version 26, vous pouvez utiliser ProcessLifecycleOwner , ajoutez-le simplement à votre dépendance comme décrit ici , par exemple:

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
}

Et puis il suffit d'interroger ProcessLifecycleOwnerquand vous le souhaitez pour l'état de l'application, exemples:

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

//Check if app is in foreground
ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
Keivan Esbati
la source
2
Merci mec, c'est la manière la meilleure et la plus simple qui convient à n'importe quelle partie de votre code spécialement lorsque vous utilisez fcm.
Mihae Kheel
si l'application est complètement fermée, que retournerait la première méthode?
Evgeniy Mishustin
@EvgeniyMishustin qui dépend de l'état actuel de l'application, mais vous verrez généralement CREATED puis DESTROYED et après cela, vous ne recevrez plus de nouveaux événements.
Keivan Esbati
Alors, où est toute instruction "IF" pour voir si l'application IF est en arrière-plan (premier plan) ???
ekashking
@ekashking vient de mettre l'intégralité de l'instruction dans la clause if. Par exemple: if (ProcessLifecycleOwner.get (). GetLifecycle (). GetCurrentState (). IsAtLeast (Lifecycle.State.STARTED)) => L'application est au premier plan
Keivan Esbati
20

Depuis Android API 16, il existe un moyen simple de vérifier si l'application est au premier plan. Ce n'est peut-être pas infaillible, mais aucune méthode sur Android n'est infaillible. Cette méthode est assez bonne à utiliser lorsque votre service reçoit une mise à jour du serveur et doit décider d'afficher ou non la notification (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;
Juozas Kontvainis
la source
ce code doit-il être dans la classe Service ou dans une autre classe, par exemple la classe Application? Merci beaucoup.
Woppi
..si vous voulez l'utiliser comme ligne finale, ce n'est qu'un booléen par rapport auquel vous pouvez vérifier.
AO_
Il s'agit de la même méthodologie que le SDK AWS Android pour les notifications push.
spakmad
Sachez que "La définition de l'arrière-plan à des fins de limitation de service est distincte de la définition utilisée par la gestion de la mémoire; une application peut être en arrière-plan en ce qui concerne la gestion de la mémoire, mais au premier plan en ce qui concerne sa capacité à lancer des services.) " developer.android.com/about/versions/oreo/background.html (
ARLabs
Merci, cela a fonctionné! J'ai pu utiliser ce code dans un JobServicepour détecter que le service s'exécute en arrière-plan.
Michael Osofsky
17

La réponse d'Idolon est sujette aux erreurs et beaucoup plus compliquée, bien que répétée ici, vérifiez que l'application Android est au premier plan ou non? et ici Déterminer 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 une activité de base 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 devez vérifier si l'une de vos activités d'application est au premier plan, vérifiez simplement isVisible() ;

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

neteinstein
la source
3
Idolon's answer is error prone- malheureusement, je dois être d'accord avec vous. Sur la base du commentaire de Dianne Hackborn dans les groupes Google, j'ai mis à jour ma réponse. Vérifiez-le s'il vous plaît pour les détails.
Idolon
2
Ce n'est pas non plus une solution infaillible. Un scénario est si l'utilisateur tiré vers le bas le panneau de notification, alors ni le onPause, onStopni l' onResumeévénement est appelé. Alors, que faites-vous alors si aucun de ces événements n'est déclenché?!
Cela m'a conduit à cette question: stackoverflow.com/questions/33657102/…
Ruchir Baronia
Malheureusement, mais ce code fonctionne mal lorsqu'une activité est lancée alors qu'un écran est éteint. Dans ce cas, onResume et onPause sont appelés making isVisible = false.
CoolMind
@CoolMind Pouvez-vous s'il vous plaît expliquer dans quel cas d'utilisation vous lanceriez une activité en arrière-plan?
neteinstein
11

J'ai essayé la solution recommandée qui utilise Application.ActivityLifecycleCallbacks et bien d'autres, mais elles n'ont pas fonctionné comme prévu. Grâce à Sarge , j'ai trouvé une solution assez 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 depuis ActivityA (et non pas call ActivityA.finish), alors ActivityB 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.

Ainsi , en fonction du comportement du cycle de vie de cette activité, vous pouvez simplement compter combien de fois onStart()et onPause()ai été appelé dans votre programme. Notez que pour chacun Activity de votre programme, vous devez remplacer onStart()et onStop(), pour incrémenter / décrémenter la variable statique utilisée pour le comptage. Vous trouverez ci-dessous le code implémentant cette logique. Notez que j'utilise une classe qui s'étend Application, alors n'oubliez pas de déclarer à l' Manifest.xmlintérieur de la balise Application:, android:name=".Utilities"bien qu'elle puisse également être implémentée à l'aide d'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 , à chaque activité de notre programme, nous devrions passer outre onStart()et onStop()et l' incrément / décrément 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'arrêts 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 < 0cela signifierait qu'il y a plus d'activités arrêtées 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 vérifier si votre application est en arrière-plan à l'intérieur onStop().

Menelaos Kotsollaris
la source
Je passerais if(Utilities.isApplicationOnBackground()) …à Utilities. Sinon, seule une activité spécifique réagira à l'événement.
Nom d'affichage
10

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. Vous devriez peut-être envisager de poser une nouvelle question sur StackOverflow, expliquant ce que vous essayez de réaliser à partir d'une expérience utilisateur, afin que nous puissions peut-être vous proposer des idées d'implémentation alternatives.

CommonsWare
la source
2
Dans Android, nous avons un paramètre appelé "Données d'arrière-plan". Ce paramètre désactive toute connexion de données d'arrière-plan lorsque l'application s'exécute en arrière-plan. Je souhaite implémenter la bascule "Données d'arrière-plan" pour mon application, donc lorsqu'aucune de mes activités n'est visible pour l'utilisateur, je voudrais que mon service arrête de faire tout transfert de données, mais au moment où l'une de mes activités reprend, je voudrais reprendre le transfert de données
cppdev
1
@cppdev: Espérons que le "transfert de données" soit effectué par a Service. Si tel est le cas, demandez à vos activités d'avertir le service dès qu'elles apparaissent et disparaissent. Si le Servicedétermine qu'aucune activité n'est visible et que cela reste 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 en ce moment, c'est inévitable AFAIK.
CommonsWare
1
Si vous voulez éviter de copier-coller le code commun entre toutes vos activités, vous pouvez créer une classe MyActivityClasshéritant Activityet mettant en œuvre les méthodes de cycle de vie, et faire hériter toutes vos activités MyActivityClass. Cela ne fonctionnera pas pour PreferenceActivityou MapActivitysi (voir cette question )
Guillaume Brunerie
@CommonsWare j'ai essayé avec OnPause () OnResume () qu'elle est active ou non mais si mon application ne s'affiche pas dans l'écran d'affichage si elle fonctionne en arrière-plan comment vérifier si elle est active ou non
Manoj
@CommonsWare j'ai essayé avec OnPause () OnResume () qu'elle est active ou non mais si mon application ne s'affiche pas dans l'écran d'affichage si elle fonctionne en arrière-plan comment vérifier si elle est active ou non
Manoj
5

Vous pouvez utiliser ComponentCallbacks2 pour détecter si l'application est en arrière-plan. BTW ce rappel est uniquement disponible dans l'API niveau 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_HIDDENalors l'application est en arrière-plan.

Vous pouvez implémenter cette interface à un activity, serviceetc.

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

   }

   @Override
   public void onLowMemory() {

   }

   @Override
   public void onTrimMemory(final int level) {
     if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
        // app is in background
     }
   }
}
Rohit Arya
la source
1
J'ai essayé votre réponse mais elle n'est pas aussi fiable. le rappel onTrimMemory ne sera pas déclenché lorsque l'écran est verrouillé ni lorsque vous appuyez sur le bouton "power" pour verrouiller l'écran. Il ne renverra pas toujours TRIM_MEMORY_UI_HIDDEN si votre application est visible et que vous ouvrez une autre application via une notification de barre d'état. La seule solution fiable consiste à implémenter ActivityLifecycleCallbacks et à l'adapter à son cas d'utilisation.
velval
4

S'appuyant sur la réponse @Cornstalks pour inclure quelques fonctionnalités utiles.

Fonctionnalités supplémentaires:

  • a introduit le modèle singleton afin que vous puissiez le faire n'importe où dans l'application: AppLifecycleHandler.isApplicationVisible () et AppLifecycleHandler.isApplicationInForeground ()
  • ajout de la gestion des événements en double (voir commentaires // prendre une action sur le changement de visibilité et // prendre une action 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 String DebugName = "AppLifecycleHandler";

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

    private static AppLifecycleHandler instance;

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

        return instance;
    }

    private AppLifecycleHandler() {
    }

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

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

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

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

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

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

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

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

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

        // take some action on change of visibility
    }

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

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

        // take some action on change of in foreground

    }

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

    public static boolean isApplicationInForeground() {
        return AppLifecycleHandler.getInstance().resumed > 0;
    }
}
Seb
la source
3

La meilleure solution que j'ai trouvée utilise des minuteries.

Vous avez démarré un temporisateur dans onPause () et annulé le même temporisateur dans onResume (), il y a 1 instance du temporisateur (généralement défini dans la classe Application). Le minuteur lui-même est configuré pour exécuter une Runnable après 2 secondes (ou tout intervalle que vous pensez 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 toutes les opérations de démarrage (par exemple, lancer les téléchargements ou activer les services de localisation).

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

Cette solution fonctionne bien si vous utilisez également un bus d'événements, car votre minuterie peut simplement déclencher un événement et différentes parties de votre application peuvent répondre en conséquence.

Andrew Kelly
la source
Je commence à penser que c'est la meilleure solution (bien que malheureuse)
dhaag23
Oui, c'est aussi la meilleure solution que j'ai gérée. Je devais arrêter la numérisation Bluetooth lorsque l'application n'était pas au premier plan, mais je ne pouvais pas simplement utiliser onpause ou arrêter ou détruire parce que je ne voulais pas arrêter et démarrer constamment lorsque l'utilisateur naviguait autour de l'application.
CaptRespect
3

Si vous activez les paramètres du développeur "Ne pas conserver les activités" - vérifiez que le nombre d'activités créées n'est pas suffisant. Vous devez également vérifier isSaveInstanceState . Ma méthode personnalisée isApplicationRunning () vérifie que 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 String TAG = AppLifecycleService.class.getName();

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

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

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

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

    private AppLifecycleService() {
    }

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

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

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

    @Override
    public void onActivityResumed(Activity activity) {   }

    @Override
    public void onActivityPaused(Activity activity) { }


    @Override
    public void onActivityStarted(Activity activity) { }

    @Override
    public void onActivityStopped(Activity activity) { }        

}
un_abonné
la source
3

La seule bonne solution:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

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

MyApp.java:

public class MyApp extends Application implements LifecycleObserver {

    public static MainActivity mainActivity = null;

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

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

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

}
Dmitry
la source
Je ne vois pas comment cette solution peut éventuellement me donner une réponse à une question simple dans une déclaration IF sur mon activité (ou fragment) si mon application est en arrière-plan ou au premier plan. Où est la déclaration "IF" ???
ekashking
2

Pour vous baser sur ce que CommonsWare et Key ont dit, vous pouvez peut-être étendre la classe Application et que toutes vos activités appellent cela sur leurs méthodes onPause / onResume. Cela vous permettrait de savoir quelle (s) activité (s) sont visibles, mais cela pourrait probablement être mieux géré.

Pouvez-vous nous expliquer exactement ce que vous avez en tête? Lorsque vous dites que vous exécutez en arrière-plan, voulez-vous simplement dire que votre application est toujours 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 active?

Dan
la source
Dans Android, nous avons un paramètre appelé "Données d'arrière-plan". Ce paramètre désactive toute connexion de données d'arrière-plan lorsque l'application s'exécute en arrière-plan. Je souhaite implémenter la bascule "Données d'arrière-plan" pour mon application, donc lorsqu'aucune de mes activités n'est visible pour l'utilisateur, j'aimerais que mon service arrête de faire tout transfert de données, mais au moment où l'une de mes activités reprend, je voudrais reprendre le transfert de données
cppdev
1
Applicationn'a pas onPause()ou onResume().
CommonsWare
1
@CommonsWare Vous avez raison, je faisais référence à chaque activité individuelle contactant l'application lors de sa pause / reprise. C'est essentiellement l'idée que vous venez de partager dans le commentaire de votre réponse, bien que vous ayez utilisé des services que j'imagine être une décision plus intelligente.
Dan
2

J'ai fait ma propre implémentation de ActivityLifecycleCallbacks. J'utilise SherlockActivity, mais pour une classe d'activité normale, cela pourrait fonctionner.

Tout d'abord, 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);
    }   
}

Quatrièmement, toutes les classes qui s'étendent de 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.

ClarkXP
la source
1

L'activité est interrompue lorsqu'un dialogue arrive au-dessus, donc toutes les solutions recommandées sont des demi-solutions. Vous devez également créer des crochets pour les boîtes de dialogue.

konmik
la source
1

Documents officiels:

Le système fait la distinction entre les applications de premier plan et d'arrière-plan. (La définition de l'arrière-plan aux fins des limitations de service est distincte de la définition utilisée par la gestion de la mémoire; une application peut être en arrière-plan en ce qui concerne la gestion de la mémoire , mais au premier plan en ce qui concerne sa capacité à lancer des services.) Une application est considéré comme au premier plan si l'une des conditions suivantes est remplie:

  1. Il a une activité visible, que l'activité soit démarrée ou suspendue.
  2. Il a un service de premier plan.
  3. Une autre application de premier plan est connectée à l'application, soit en se liant à l'un de ses services, soit en utilisant l'un de ses fournisseurs de contenu. Par exemple, l'application est au premier plan si une autre application se lie à son:
    • IME
    • Service de papier peint
    • Écouteur de notification
    • Service voix ou texte

Si aucune de ces conditions n'est remplie, l'application est considérée comme étant en arrière-plan.

Uddhav Gautam
la source
0

Une autre solution pour cet ancien poste (pour ceux qu'il pourrait aider):


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

public class BaseApplication extends Application {

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

    private Map<Activity, Status> activities;

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

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

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

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

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

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

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

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

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

}

public class MyActivity extends BaseActivity {...}

public class BaseActivity extends Activity {

    private BaseApplication application;

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

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

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

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

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

}
alex
la source
0

Voir le commentaire dans la fonction onActivityDestroyed.

Fonctionne avec la version cible du SDK 14>:

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

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {

    public static int active = 0;

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

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

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

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

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

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

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

    }

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

Vous devez utiliser une préférence partagée pour stocker la propriété et agir en conséquence en utilisant la liaison de service de vos activités. Si vous utilisez uniquement la liaison (c'est-à-dire jamais utiliser startService), votre service ne s'exécutera que lorsque vous vous y lierez (lier onResume et annuler la liaison onPause), ce qui le fera fonctionner uniquement au premier plan et si vous voulez travailler sur arrière-plan, vous pouvez utiliser le service de démarrage / arrêt régulier.

Ofek Ron
la source
0

Je pense que cette question devrait être plus claire. Quand? Où? Quelle est votre situation spécifique que vous souhaitez savoir si votre application est en arrière-plan?

Je viens de présenter ma solution à ma façon.
J'obtiens cela en utilisant le champ "importance" de la RunningAppProcessInfoclasse dans la onStopméthode de chaque activité dans mon application, ce qui peut être simplement réalisé en fournissant un BaseActivitypour d'autres activités à étendre qui implémente la onStopméthode pour vérifier la valeur de "l'importance". Voici le code:

public static boolean isAppRunning(Context context) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> appProcesses = activityManager
        .getRunningAppProcesses();
    for (RunningAppProcessInfo appProcess : appProcesses) {
        if (appProcess.processName.equals(context.getPackageName())) {
            if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {
                return true;
            } 
        }
    }
    return false;
}
wxf04125
la source
Ce n'est pas une solution recommandée comme indiqué dans la réponse de @ Idolon.
CoolMind
0

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

Bref, votre activité n'est plus visible après onStop()avoir été appelée.

Clé
la source
3
J'ai environ 10 activités dans ma candidature. Je veux donc savoir si aucun d'entre eux n'est visible par l'utilisateur. Dans l'ensemble, je veux savoir si mon application dans son ensemble s'exécute en arrière
cppdev
Ainsi, vous gardez une trace de tous les 10 ish. Ou, comme CommonsWare l'a suggéré, expliquez ce que vous essayez de faire.
Clé
3
Ce n'est pas correct. Votre activité est visible jusqu'à onStop; entre onPauseet onStopc'est visible , mais pas au premier plan .
nickgrim
@nickgrim: qu'est-ce qui n'est pas correct? J'ai déclaré qu'une activité n'est plus visible après l' onStop()appel, ce qui correspond à ce que vous avez écrit.
Clé
@Key: Vous aviez initialement dit jusqu'à ce onPausequ'on appelle: une modification récente vous a corrigé.
nickgrim
0

Qu'en est-il de l'utilisation de getApplicationState (). IsInForeground ()?

Jonathan
la source
0

À mon avis, de nombreuses réponses introduisent une lourde charge de code et apportent beaucoup de complexité et de non-lisibilité.

Lorsque les gens demandent à SO comment communiquer entre un Serviceet un Activity, je conseille généralement d'utiliser le LocalBroadcastManager .


Pourquoi?

Eh bien, en citant les documents:

  • Vous savez que les données que vous diffusez ne quitteront pas votre application, alors ne vous inquiétez pas des fuites de données privées.

  • Il n'est pas possible pour d'autres applications d'envoyer ces diffusions à votre application, vous n'avez donc pas à vous soucier des failles de sécurité qu'elles peuvent exploiter.

  • C'est plus efficace que d'envoyer une diffusion globale via le système.

Pas dans la documentation:

  • Il ne nécessite pas de bibliothèques externes
  • Le code est minimal
  • Il est rapide à mettre en œuvre et à comprendre
  • Aucun rappel personnalisé auto-implémenté / modèle ultra-singleton / intra-processus que ce soit ...
  • Pas de fortes références sur Activity, Application...

La description

Donc, vous voulez vérifier si l'un des éléments Activityest actuellement au premier plan. Vous faites généralement cela dans un Serviceou dans votre Applicationclasse.

Cela signifie que vos Activityobjets deviennent l'expéditeur d'un signal (je suis activé / désactivé). Votre Service, d'autre part, devient le Receiver.

Il y a deux moments où votre Activityvous dit si ça va au premier plan ou en arrière-plan (oui seulement deux ... pas 6).

Lorsque le Activitypasse au premier plan, la onResume()méthode est déclenchée (également appelée après onCreate()).

Lorsque le Activityva à l'arrière, onPause()est appelé.

Ce sont les moments où vous devez Activityenvoyer le signal à votre Servicepour décrire son état.

En cas de multiples Activity, n'oubliez pas que le premier Activitypasse en arrière-plan, puis un autre apparaît au premier plan.

La situation serait donc: *

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

Le Service/ Applicationcontinuera simplement à écouter ces signaux et agira en conséquence.


Code (TLDR)

Vous Servicedevez mettre BroadcastReceiveren œuvre un afin d'écouter les signaux.

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

Inscrivez - le ReceiverdansService::onCreate()

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

Désinscrivez-le dans Service::onDestroy()

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

Vous Activitydevez maintenant communiquer leur état.

Dans Activity::onResume()

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

Dans Activity::onPause()

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

Une situation très, très courante

Développeur: Je souhaite envoyer des données depuis mon site Serviceet mettre à jour le Activity. Comment vérifier si le Activityest au premier plan?

Il n'est généralement pas nécessaire de vérifier si le texte Activityest au premier plan ou non. Envoyez simplement les données via LocalBroadcastManagervotre Service. Si le Activityest allumé, il répondra et agira.

Pour cette situation très courante, le Servicedevient l'expéditeur et le Activitymet en œuvre le BroadcastReceiver.

Alors, créez un Receiverdans votre Activity. Enregistrez-le onResume()et annulez-le onPause(). Il n'est pas nécessaire d'utiliser les autres méthodes du cycle de vie .

Définissez le Receivercomportement dans onReceive()(mettez à jour ListView, faites ceci, faites cela, ...).

De cette façon, l' Activityécoute n'interviendra que si elle est au premier plan et rien ne se passera si elle est à l'arrière ou détruite.

En cas de multiples Activity, celui qui Activityest activé répondra (s'il implémente également le Receiver).

Si tous sont en arrière-plan, personne ne répondra et le signal sera simplement perdu.

Envoyez les données depuis le Servicevia Intent(voir le code ci-dessus) en spécifiant l'ID du signal.


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

    val appProcesses = activityManager.runningAppProcesses ?: return false

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

    return false
}
ayoub laaziz
la source
0

Aucune des réponses ne correspondait parfaitement au cas spécifique si vous cherchez à savoir si une activité spécifique est en cours et si vous êtes un SDK sans accès direct à l'application. Pour moi, j'étais dans le fil d'arrière-plan, je venais de recevoir une notification push pour un nouveau message de chat et je ne veux afficher une notification système que si l'écran de chat n'est pas au premier plan.

En utilisant ActivityLifecycleCallbackscela comme recommandé dans d'autres réponses, j'ai créé une petite classe util qui contient la logique pour savoir si elle MyActivityest au premier plan ou non.

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

private var isMyActivityInForeground = false

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

fun isMyActivityForeground() = isMyActivityInForeground

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

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

}

scottyab
la source
-1

Dans mes activités onResume et onPause, j'écris un booléen isVisible dans SharedPrefences.

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

Et lisez-le ailleurs en cas de besoin 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)){...}

Peut-être pas élégant, mais ça marche pour moi ...

mtwagner
la source
-1

Il est peut-être trop tard pour répondre, mais si quelqu'un vient nous rendre visite, voici la solution que je suggère. Pour afficher des toasts et des notifications lorsque l'utilisateur est en BG. 2.Pour effectuer certaines tâches pour la première fois, l'utilisateur vient de BG, comme un sondage, un nouveau dessin, etc.

La solution d'Idolon et d'autres s'occupe de la première partie, mais pas de la seconde. S'il y a plusieurs activités dans votre application et que l'utilisateur bascule entre elles, au moment où vous êtes en deuxième activité, l'indicateur visible sera faux. Il ne peut donc pas être utilisé de façon déterministe.

J'ai fait quelque chose qui a été suggéré par CommonsWare, "Si le service détermine qu'aucune activité n'est visible, et cela reste ainsi pendant un certain temps , arrêtez le transfert de données au prochain point d'arrêt logique."

La ligne en gras est importante et peut être utilisée pour obtenir le deuxième élément. Donc, ce que je fais, c'est une fois que j'obtiens onActivityPaused (), ne changez pas le visible en faux directement, ayez plutôt un minuteur de 3 secondes (c'est le maximum que la prochaine activité devrait être lancée), et s'il n'y a pas onActivityResumed ( ) appeler dans les 3 secondes suivantes, changer visible en faux. De même dans onActivityResumed () s'il y a un temporisateur, je l'annule. Pour résumer, le visible devient isAppInBackground.

Désolé, impossible de copier-coller le code ...

Sriram
la source
-3

J'aimerais vous recommander d'utiliser une autre façon de procéder.

Je suppose que vous voulez afficher l'écran de démarrage pendant le démarrage du programme, s'il est déjà exécuté en backend, ne le montrez pas.

Votre application peut écrire en permanence l'heure actuelle dans un fichier spécifique. Pendant que votre application démarre, vérifiez le dernier horodatage, si current_time-last_time> la plage de temps que vous avez spécifiée pour écrire la dernière heure, cela signifie que votre application est arrêtée, soit tuée par le système ou par l'utilisateur lui-même.

user2322866
la source