Par arrière-plan, je veux dire qu'aucune des activités de l'application n'est actuellement visible pour l'utilisateur?
Par arrière-plan, je veux dire qu'aucune des activités de l'application n'est actuellement visible pour l'utilisateur?
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
, des Activity.onResume
méthodes. Stockez le statut de "visibilité" dans une autre classe. Les bons choix sont votre propre implémentation du Application
ou 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 onPause
et onResume
à chacun Activity
dans le projet (vous pouvez créer un ancêtre commun pour vos activités si vous le souhaitez, mais si votre activité est déjà étendue à partir de MapActivity
/ ListActivity
etc., vous devez toujours écrire ce qui suit à la main):
@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
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'RunningAppProcessInfo
enregistrements. Pour déterminer si votre application est au premier plan, vérifiezRunningAppProcessInfo.importance
que l'égalité àRunningAppProcessInfo.IMPORTANCE_FOREGROUND
whileRunningAppProcessInfo.processName
est é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'importanceIMPORTANCE_FOREGROUND
de votre tâche, qu'elle soit réellement au premier plan ou non. Appelez-le dans le thread d'arrière-plan (par exemple viaAsyncTask
) 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.getRunningTasks
pour sa isApplicationBroughtToBackground
méthode. Voir le commentaire de Dianne ci-dessus et n'utilisez pas non plus cette méthode.
OnStop
demande de isActivityVisible
.
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.
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 MyLifecycleHandler
seront réinitialisées 0
.
Est-ce que cela fonctionne pour les changements de configuration?
Par défaut, non. Vous devez définir explicitement configChanges=orientation|screensize
( |
avec tout ce que vous voulez) dans votre fichier manifeste et gérer les modifications de configuration, sinon votre activité sera détruite et recréée. Si vous ne 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 configChanges
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è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 Activity
de » onStop()
méthode après super.onStop()
.
onStop()
après super.onStop()
. Ne vérifiez pas l'arrière-plan onPause()
.
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
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 les multiple 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:
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 ProcessLifecycleOwner
quand 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);
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;
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:
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
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.
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 ActivityBonStart()
sera appelé avant ActivityAonStop()
.
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.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.
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:
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()
.
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.
Service
. Si tel est le cas, demandez à vos activités d'avertir le service dès qu'elles apparaissent et disparaissent. Si le Service
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éritant Activity
et mettant en œuvre les méthodes de cycle de vie, et faire hériter toutes vos activités MyActivityClass
. Cela ne fonctionnera pas pour PreferenceActivity
ou MapActivity
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.
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
}
}
}
S'appuyant sur la réponse @Cornstalks pour inclure quelques fonctionnalités utiles.
Fonctionnalités supplémentaires:
App.java
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
}
}
AppLifecycleHandler.java
public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
private int resumed;
private int started;
private final String DebugName = "AppLifecycleHandler";
private boolean isVisible = false;
private boolean isInForeground = false;
private static AppLifecycleHandler instance;
public static AppLifecycleHandler getInstance() {
if (instance == null) {
instance = new AppLifecycleHandler();
}
return instance;
}
private AppLifecycleHandler() {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
++resumed;
android.util.Log.w(DebugName, "onActivityResumed -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}
@Override
public void onActivityPaused(Activity activity) {
--resumed;
android.util.Log.w(DebugName, "onActivityPaused -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityStarted(Activity activity) {
++started;
android.util.Log.w(DebugName, "onActivityStarted -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}
@Override
public void onActivityStopped(Activity activity) {
--started;
android.util.Log.w(DebugName, "onActivityStopped -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}
private void setVisible(boolean visible) {
if (isVisible == visible) {
// no change
return;
}
// visibility changed
isVisible = visible;
android.util.Log.w(DebugName, "App Visiblility Changed -> application is visible: " + isVisible);
// take some action on change of visibility
}
private void setForeground(boolean inForeground) {
if (isInForeground == inForeground) {
// no change
return;
}
// in foreground changed
isInForeground = inForeground;
android.util.Log.w(DebugName, "App In Foreground Changed -> application is in foreground: " + isInForeground);
// take some action on change of in foreground
}
public static boolean isApplicationVisible() {
return AppLifecycleHandler.getInstance().started > 0;
}
public static boolean isApplicationInForeground() {
return AppLifecycleHandler.getInstance().resumed > 0;
}
}
La 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.
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) { }
}
La seule bonne solution:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
MyApp.mainActivity = this;
super.onCreate(savedInstanceState);
...
}
public class MyApp extends Application implements LifecycleObserver {
public static MainActivity mainActivity = null;
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void onAppBackgrounded() {
// app in background
if (mainActivity != null) {
...
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void onAppForegrounded() {
// app in foreground
if (mainActivity != null) {
...
}
}
}
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?
Application
n'a pas onPause()
ou onResume()
.
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.
Puisqu'il n'est pas déjà mentionné, je proposerai aux lecteurs d'explorer ProcessLifecycleOwner disponible via les composants de l'architecture Android
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.
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();
}
}
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++;
}
}
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 RunningAppProcessInfo
classe dans la onStop
méthode de chaque activité dans mon application, ce qui peut être simplement réalisé en fournissant un BaseActivity
pour d'autres activités à étendre qui implémente la onStop
mé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;
}
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.
onStop
; entre onPause
et onStop
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é.
À 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 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:
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 un Service
ou dans votre Application
classe.
Cela signifie que vos Activity
objets 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 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, la onResume()
méthode est déclenchée (également appelée après onCreate()
).
Lorsque le Activity
va à l'arrière, onPause()
est appelé.
Ce sont les moments où vous devez Activity
envoyer le signal à votre Service
pour décrire son état.
En cas de multiples Activity
, n'oubliez pas que le premier Activity
passe 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
/ Application
continuera simplement à écouter ces signaux et agira en conséquence.
Code (TLDR)
Vous Service
devez mettre BroadcastReceiver
en œ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 Receiver
dansService::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 Activity
devez 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
Service
et mettre à jour leActivity
. Comment vérifier si leActivity
est au premier plan?
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 via LocalBroadcastManager
votre Service
. Si le Activity
est allumé, il répondra et agira.
Pour cette situation très courante, le Service
devient l'expéditeur et le Activity
met en œuvre le BroadcastReceiver
.
Alors, créez un Receiver
dans 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 Receiver
comportement 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 Activity
est 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 Service
via Intent
(voir le code ci-dessus) en spécifiant l'ID du signal.
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
}
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 elle MyActivity
est 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
}
}
}
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 ...
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 ...
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.