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. : /
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:
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):
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):
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.
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.
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.
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 ...
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:
publicclassMyLifecycleHandlerimplementsActivityLifecycleCallbacks{// 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.privateint resumed;privateint paused;privateint started;privateint stopped;@Overridepublicvoid onActivityCreated(Activity activity,Bundle savedInstanceState){}@Overridepublicvoid onActivityDestroyed(Activity activity){}@Overridepublicvoid onActivityResumed(Activity activity){++resumed;}@Overridepublicvoid onActivityPaused(Activity activity){++paused;
android.util.Log.w("test","application is in foreground: "+(resumed > paused));}@Overridepublicvoid onActivitySaveInstanceState(Activity activity,Bundle outState){}@Overridepublicvoid onActivityStarted(Activity activity){++started;}@Overridepublicvoid 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" ...publicclassMyApplicationextendsApplication{@Overridepublicvoid 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(newMyLifecycleHandler());}}
@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èssuper.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èssuper.onStop() .
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:
classArchLifecycleApp: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:
publicclassArchLifecycleAppextendsApplicationimplementsLifecycleObserver{@Overridepublicvoid onCreate(){super.onCreate();ProcessLifecycleOwner.get().getLifecycle().addObserver(this);}@OnLifecycleEvent(Lifecycle.Event.ON_STOP)publicvoid onAppBackgrounded(){//App in background}@OnLifecycleEvent(Lifecycle.Event.ON_START)publicvoid 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'}}}
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 backgroundProcessLifecycleOwner.get().getLifecycle().getCurrentState()==Lifecycle.State.CREATED;//Check if app is in foregroundProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
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).
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.
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é?!
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 chacunActivity 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.
publicclassUtilitiesextendsApplication{privatestaticint stateCounter;publicvoid onCreate(){super.onCreate();
stateCounter =0;}/**
* @return true if application is on background
* */publicstaticboolean isApplicationOnBackground(){return stateCounter ==0;}//to be called on each Activity onStart()publicstaticvoid activityStarted(){
stateCounter++;}//to be called on each Activity onStop()publicstaticvoid 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:
@Overridepublicvoid onStart(){super.onStart();Utilities.activityStarted();}@Overridepublicvoid 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:
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.
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().
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.
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.
publicclassMainActivityextendsAppCompatActivityimplementsComponentCallbacks2{@Overridepublicvoid onConfigurationChanged(finalConfiguration newConfig){}@Overridepublicvoid onLowMemory(){}@Overridepublicvoid onTrimMemory(finalint level){if(level ==ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){// app is in background}}}
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)
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.
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:
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?
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:
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.
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:
Il a une activité visible, que l'activité soit démarrée ou suspendue.
Il a un service de premier plan.
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.
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.
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:
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 ()?
À 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.
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 =newBroadcastReceiver(){@Overridepublicvoid onReceive(Context context,Intent intent){// received data if Activity is on / off}}publicstaticfinalIntentFilter SIGNAL_FILTER =newIntentFilter("com.you.yourapp.MY_SIGNAL")
@Overrideprotectedvoid 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 =newIntent();
intent.setAction(SomeActivity.SIGNAL_FILTER);// put ON boolean in intent LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
Dans Activity::onPause()
Intent intent =newIntent();
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.
Sauf pour le support multi-fenêtres . Cela peut être délicat (veuillez le tester si nécessaire) ...
fun isAppInForeground():Boolean{
val activityManager = getSystemService(Context.ACTIVITY_SERVICE)asActivityManager?:returnfalse
val appProcesses = activityManager.runningAppProcesses ?:returnfalse
val packageName = packageName
for(appProcess in appProcesses){if(appProcess.importance ==ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName){returntrue}}returnfalse}
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.
classMyActivityMonitor(context:Context):Application.ActivityLifecycleCallbacks{privatevar isMyActivityInForeground =false
init {(context.applicationContext asApplication).registerActivityLifecycleCallbacks(this)}
fun isMyActivityForeground()= isMyActivityInForeground
override fun onActivityPaused(activity:Activity?){if(activity isMyActivity){
isMyActivityInForeground =false}}override fun onActivityResumed(activity:Activity?){if(activity isMyActivity){
isMyActivityInForeground =true}}
// 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 ...
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.
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.
Réponses:
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:
La bonne solution (crédits vont à Dan , CommonsWare et NeTeInStEiN )
visibilité piste de votre application par vous - même à l' aide
Activity.onPause
, desActivity.onResume
méthodes. Stockez le statut de "visibilité" dans une autre classe. Les bons choix sont votre propre implémentation duApplication
ou duService
(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ée
Application
classe (notez laisActivityVisible()
méthode statique):Enregistrez votre classe d'application dans
AndroidManifest.xml
:Ajoutez
onPause
etonResume
à chacunActivity
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 deMapActivity
/ListActivity
etc., vous devez toujours écrire ce qui suit à la main):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.
Le mauvais
j'ai utilisée pour suggérer la solution suivante:
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 :
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.
Une autre mauvaise solution que la bibliothèque
Droid-Fu mentionnée dans l'une des réponses utilise
ActivityManager.getRunningTasks
pour saisApplicationBroughtToBackground
méthode. Voir le commentaire de Dianne ci-dessus et n'utilisez pas non plus cette méthode.la source
OnStop
demande deisActivityVisible
.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 chaqueActivity
élément de votre application pour définir / supprimer quelque chose dansonResumed()
/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:
MyApplication.java:
@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: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
MyLifecycleHandler
seront réinitialisées0
.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 deconfigChanges
manière à ce que votre activité ne soit pas détruite. Heureusement, j'ai dû réglerconfigChanges
dé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
Activity
de »onPause()
méthode aprèssuper.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
Activity
de »onStop()
méthode aprèssuper.onStop()
.la source
onStop()
aprèssuper.onStop()
. Ne vérifiez pas l'arrière-planonPause()
.SOLUTION GOOGLE - pas un hack, comme les solutions précédentes. Utilisez ProcessLifecycleOwner
Kotlin:
Java:
dans app.gradle
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
la source
companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }
alors vous pouvez obtenir l'état de premier plan avecArchLifecycleApp.isForeground()
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 lesmultiple processes
applications, existe-t-il des API que nous pouvons réaliser avec élégance?À 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:
Et puis il suffit d'interroger
ProcessLifecycleOwner
quand vous le souhaitez pour l'état de l'application, exemples:la source
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).
la source
JobService
pour détecter que le service s'exécute en arrière-plan.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:
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
la source
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.onPause
,onStop
ni l'onResume
événement est appelé. Alors, que faites-vous alors si aucun de ces événements n'est déclenché?!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.
C'est aussi la principale différence entre
onStop()
etonPause()
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()
etonPause()
ai été appelé dans votre programme. Notez que pour chacunActivity
de votre programme, vous devez remplaceronStart()
etonStop()
, 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'étendApplication
, alors n'oubliez pas de déclarer à l'Manifest.xml
inté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.Maintenant , à chaque activité de notre programme, nous devrions passer outre
onStart()
etonStop()
et l' incrément / décrément comme indiqué ci - dessous:Avec cette logique, il y a 2 cas possibles:
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.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
cela 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()
.la source
if(Utilities.isApplicationOnBackground()) …
àUtilities
. Sinon, seule une activité spécifique réagira à l'événement.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.
la source
Service
. Si tel est le cas, demandez à vos activités d'avertir le service dès qu'elles apparaissent et disparaissent. Si leService
dé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.MyActivityClass
héritantActivity
et mettant en œuvre les méthodes de cycle de vie, et faire hériter toutes vos activitésMyActivityClass
. Cela ne fonctionnera pas pourPreferenceActivity
ouMapActivity
si (voir cette question )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_HIDDEN
alors l'application est en arrière-plan.Vous pouvez implémenter cette interface à un
activity
,service
etc.la source
S'appuyant sur la réponse @Cornstalks pour inclure quelques fonctionnalités utiles.
Fonctionnalités supplémentaires:
App.java
AppLifecycleHandler.java
la source
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.
la source
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:
la source
La seule bonne solution:
MainActivity.java:
MyApp.java:
la source
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?
la source
Application
n'a pasonPause()
ouonResume()
.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:
Deuxièmement, j'ai implémenté cette interface dans la classe de mon application:
Troisièmement, je crée une classe qui s'étend de SherlockActivity:
Quatrièmement, toutes les classes qui s'étendent de SherlockActivity, j'ai remplacé MySherlockActivity:
Maintenant, dans le logcat, vous verrez les journaux programmés dans l'implémentation de l'interface faite dans MyApplication.
la source
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.
la source
Puisqu'il n'est pas déjà mentionné, je proposerai aux lecteurs d'explorer ProcessLifecycleOwner disponible via les composants de l'architecture Android
la source
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:
Si aucune de ces conditions n'est remplie, l'application est considérée comme étant en arrière-plan.
la source
Une autre solution pour cet ancien poste (pour ceux qu'il pourrait aider):
la source
Voir le commentaire dans la fonction onActivityDestroyed.
Fonctionne avec la version cible du SDK 14>:
la source
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.
la source
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
RunningAppProcessInfo
classe dans laonStop
méthode de chaque activité dans mon application, ce qui peut être simplement réalisé en fournissant unBaseActivity
pour d'autres activités à étendre qui implémente laonStop
méthode pour vérifier la valeur de "l'importance". Voici le code:la source
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.la source
onStop
; entreonPause
etonStop
c'est visible , mais pas au premier plan .onStop()
appel, ce qui correspond à ce que vous avez écrit.onPause
qu'on appelle: une modification récente vous a corrigé.Qu'en est-il de l'utilisation de getApplicationState (). IsInForeground ()?
la source
À 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
Service
et unActivity
, je conseille généralement d'utiliser le LocalBroadcastManager .Pourquoi?
Eh bien, en citant les documents:
Pas dans la documentation:
Activity
,Application
...La description
Donc, vous voulez vérifier si l'un des éléments
Activity
est actuellement au premier plan. Vous faites généralement cela dans unService
ou dans votreApplication
classe.Cela signifie que vos
Activity
objets deviennent l'expéditeur d'un signal (je suis activé / désactivé). VotreService
, d'autre part, devient leReceiver
.Il y a deux moments où votre
Activity
vous dit si ça va au premier plan ou en arrière-plan (oui seulement deux ... pas 6).Lorsque le
Activity
passe au premier plan, laonResume()
méthode est déclenchée (également appelée aprèsonCreate()
).Lorsque le
Activity
va à l'arrière,onPause()
est appelé.Ce sont les moments où vous devez
Activity
envoyer le signal à votreService
pour décrire son état.En cas de multiples
Activity
, n'oubliez pas que le premierActivity
passe en arrière-plan, puis un autre apparaît au premier plan.La situation serait donc: *
Le
Service
/Application
continuera simplement à écouter ces signaux et agira en conséquence.Code (TLDR)
Vous
Service
devez mettreBroadcastReceiver
en œuvre un afin d'écouter les signaux.Inscrivez - le
Receiver
dansService::onCreate()
Désinscrivez-le dans
Service::onDestroy()
Vous
Activity
devez maintenant communiquer leur état.Dans
Activity::onResume()
Dans
Activity::onPause()
Une situation très, très courante
Il n'est généralement pas nécessaire de vérifier si le texte
Activity
est au premier plan ou non. Envoyez simplement les données viaLocalBroadcastManager
votreService
. Si leActivity
est allumé, il répondra et agira.Pour cette situation très courante, le
Service
devient l'expéditeur et leActivity
met en œuvre leBroadcastReceiver
.Alors, créez un
Receiver
dans votreActivity
. Enregistrez-leonResume()
et annulez-leonPause()
. Il n'est pas nécessaire d'utiliser les autres méthodes du cycle de vie .Définissez le
Receiver
comportement dansonReceive()
(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 quiActivity
est activé répondra (s'il implémente également leReceiver
).Si tous sont en arrière-plan, personne ne répondra et le signal sera simplement perdu.
Envoyez les données depuis le
Service
viaIntent
(voir le code ci-dessus) en spécifiant l'ID du signal.la source
la source
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
ActivityLifecycleCallbacks
cela comme recommandé dans d'autres réponses, j'ai créé une petite classe util qui contient la logique pour savoir si elleMyActivity
est au premier plan ou non.}
la source
Dans mes activités onResume et onPause, j'écris un booléen isVisible dans SharedPrefences.
Et lisez-le ailleurs en cas de besoin via,
Peut-être pas élégant, mais ça marche pour moi ...
la source
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 ...
la source
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.
la source