442 votes

Comment détecter quand une application Android passe en arrière-plan et revient au premier plan

J'essaie d'écrire une application qui fait quelque chose de spécifique quand elle est ramenée au premier plan après un certain temps. Existe-t-il un moyen de détecter quand une application est envoyée en arrière-plan ou amenée au premier plan?

230voto

Martín Marconcini Points 7420

Avertissement: ce n'est bon que si vous êtes en développement pour ICE CREAM SANDWICH (NIVEAU API 14)

Il peut être en retard mais il y a une méthode fiable de Sandwich à la Crème Glacée (API 14) et au-Dessus.

S'avère que lorsque votre application n'a pas plus visible de l'INTERFACE utilisateur, un rappel est déclenché. La fonction de rappel, vous pouvez mettre en œuvre dans une classe personnalisée, est appelé ComponentCallbacks2 (oui, deux). Ce rappel est uniquement disponible dans l'API de Niveau 14 (Ice Cream Sandwich) et au-dessus.

Vous obtiendrez un appel à la méthode:

public abstract void onTrimMemory (int level)

Le Niveau est de 20 ou plus précisément

public static final int TRIM_MEMORY_UI_HIDDEN

Je l'ai testé et il fonctionne toujours, car le niveau 20 est juste une "suggestion" de ce que vous pourriez voulez libérer certaines ressources depuis votre application n'est plus visible.

Pour citer les docs officielles:

Niveau pour onTrimMemory(int): le processus a été montrant une interface utilisateur, et n'est plus à faire. Les grandes affectations avec l'INTERFACE utilisateur doit être libéré à ce point pour permettre de mémoire pour être mieux gérés.

Bien sûr, vous devez mettre en œuvre cette de le faire réellement ce qu'il dit (purge de la mémoire qui n'a pas été utilisée en certain temps, quelques collections qui ont été assis utilisé, etc. Les possibilités sont infinies (voir les docs officielles pour d'autres plus critiques niveaux).

Mais la chose intéressante, c'est que l'OS est de vous dire: HEY, votre application est allé à l'arrière-plan!

Ce qui est exactement ce que vous avez voulu savoir en premier lieu.

Comment déterminez-vous quand vous avez de nouveau?

Eh bien, c'est facile, je suis sûr que vous avez un "BaseActivity" de sorte que vous pouvez utiliser votre onResume() pour marquer le fait que vous êtes de retour. Parce que la seule fois où vous serez en train de dire que vous n'êtes pas dos est en fait, quand vous recevez un appel de ce qui précède onTrimMemory méthode.

Elle fonctionne. Vous n'avez pas obtenir de faux positifs. Si une activité est reprise, vous êtes de retour, 100% du temps. Si l'utilisateur passe à l'arrière à nouveau, vous obtenez un autre onTrimMemory() appel.

Vous devez inscrire vos Activités (ou mieux encore, une classe personnalisée).

La meilleure façon de garantir que vous recevez toujours ce problème est de créer une classe simple comme ceci:

public class MemoryBoss 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) {
            // We're in the Background
        }
        // you might as well implement some memory cleanup here and be a nice Android dev.
    }
}

Pour l'utiliser, dans votre Application de mise en œuvre (vous en avez un, NON?), faire quelque chose comme:

MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
   super.onCreate();
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
      mMemoryBoss = new MemoryBoss();
      registerComponentCallbacks(mMemoryBoss);
   } 
}

Si vous créez un Interface vous pouvez ajouter un else que if et de mettre en oeuvre ComponentCallbacks (sans le 2) utilisé dans ce qui suit API 14. Que de rappel uniquement a l' onLowMemory() méthode et n'est pas appelé quand vous allez à l'arrière-plan, mais vous devez l'utiliser pour la garniture de la mémoire.

Maintenant, lancez votre Application et appuyez sur home. Votre onTrimMemory(final int level) méthode doit être appelée (astuce: ajouter un enregistrement).

La dernière étape est de se désinscrire de la fonction de rappel. Probablement le meilleur endroit est l' onTerminate() méthode de votre Application, mais, cette méthode n'est pas appelée sur un périphérique réel:

/**
 * This method is for use in emulated process environments.  It will
 * never be called on a production Android device, where processes are
 * removed by simply killing them; no user code (including this callback)
 * is executed when doing so.
 */

Donc, sauf si vous avez vraiment une situation où vous ne voulez plus être inscrit, vous pouvez la sécurité de l'ignorer, depuis votre processus est en train de mourir au niveau de l'OS de toute façon.

Si vous décidez d'annuler l'inscription à un certain point (si vous avez, par exemple, de fournir un dispositif d'interruption de votre application pour nettoyer et mourir), vous pouvez le faire:

unregisterComponentCallbacks(mMemoryBoss);

Et c'est tout.

178voto

d60402 Points 860

Voici comment j'ai réussi à résoudre ce problème. Il fonctionne sur le principe que l'utilisation d'une référence de temps entre l'activité des transitions seront plus susceptibles de fournir des preuves suffisantes qu'une application a été "backgrounded" ou pas.

Tout d'abord, j'ai utilisé un android.app.L'instance de l'Application (nous allons l'appeler MyApplication) qui dispose d'une Minuterie, d'un TimerTask, une constante pour représenter le nombre maximum de millisecondes que la transition d'une activité à l'autre, pourraient raisonnablement prendre (je suis allé avec une valeur de 2s), et un booléen pour indiquer si l'application a été "arrière-plan":

public class MyApplication extends Application {

    private Timer mActivityTransitionTimer;
    private TimerTask mActivityTransitionTimerTask;
    public boolean wasInBackground;
    private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
    ...

L'application fournit également deux méthodes pour le démarrage et l'arrêt de la minuterie/tâche:

public void startActivityTransitionTimer() {
    this.mActivityTransitionTimer = new Timer();
    this.mActivityTransitionTimerTask = new TimerTask() {
        public void run() {
            MyApplication.this.wasInBackground = true;
        }
    };

    this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
                                           MAX_ACTIVITY_TRANSITION_TIME_MS);
}

public void stopActivityTransitionTimer() {
    if (this.mActivityTransitionTimerTask != null) {
        this.mActivityTransitionTimerTask.cancel();
    }

    if (this.mActivityTransitionTimer != null) {
        this.mActivityTransitionTimer.cancel();
    }

    this.wasInBackground = false;
}

La dernière pièce de cette solution est d'ajouter un appel à chacune de ces méthodes à partir de la onResume() et onPause() les événements de toutes les activités ou, de préférence, dans une base de l'Activité à partir de laquelle l'ensemble de vos Activités concrètes hériter:

@Override
public void onResume()
{
    super.onResume();

    MyApplication myApp = (MyApplication)this.getApplication();
    if (myApp.wasInBackground)
    {
        //Do specific came-here-from-background code
    }

    myApp.stopActivityTransitionTimer();
}

@Override
public void onPause()
{
    super.onPause();
    ((MyApplication)this.getApplication()).startActivityTransitionTimer();
}

Donc, dans le cas où l'utilisateur est tout simplement naviguer entre les activités de votre application, le onPause() de la sortie de l'activité démarre le timer, mais presque immédiatement, la nouvelle activité étant entré annule la minuterie avant de pouvoir atteindre le max de temps de transition. Et donc wasInBackground serait faux.

En revanche, lorsque l'Activité est à l'avant-plan de l'écran de lancement, de réactivation de l'appareil à la fin de l'appel de téléphone, etc., plus que probablement, la minuterie de la tâche exécutée avant cet événement, et donc wasInBackground a été mis à vrai.

112voto

Swind Points 409

L' onPause() et onResume() méthodes sont appelées lorsque la demande est introduite à l'arrière-plan et au premier plan à nouveau. Cependant, ils sont également appelés lorsque l'application est démarrée pour la première fois, et avant qu'il soit tué. Vous pouvez en lire plus dans l'Activité.

Il n'y a pas d'approche directe pour obtenir le statut de la demande, tandis que dans l'arrière-plan ou au premier plan, mais même j'ai été confronté à ce problème et trouvé la solution avec onWindowFocusChanged et onStop.

Pour plus de détails, consultez ici Android: la Solution pour détecter quand une application Android va à fond et revenir à la premier plan sans getRunningTasks ou getRunningAppProcesses.

64voto

Gnawer Points 147

Nous utilisons cette méthode. Il a l'air trop simple pour travailler, mais il a été bien testé dans notre application et, en fait, fonctionne étonnamment bien dans tous les cas, y compris à l'écran d'accueil par le bouton "home", par le bouton "retour", ou après le verrouillage de l'écran. Lui donner un essai.

L'idée est, lorsque en avant-plan, Android commence toujours nouvelle activité juste avant l'arrêt précédent. Ce n'est pas garanti, mais c'est la façon dont il fonctionne. BTW, le Déluge semble utiliser la même logique (juste une supposition, je ne l'ai pas vérifié, mais il s'accroche à un même événement).

public abstract class BaseActivity extends Activity {

    private static int sessionDepth = 0;

    @Override
    protected void onResume() {
        sessionDepth++;
    }

    @Override
    protected void onStop() {
        if (sessionDepth > 0)
            sessionDepth--;
        if (sessionDepth == 0) {
            // app went to background
        }
    }

}

55voto

Emil Points 599

Si votre application se compose de plusieurs activites et/ou empilés activités comme un onglet de la barre de widget, puis en substituant onPause() et onResume() ne fonctionnera pas. I. e lors du démarrage d'une nouvelle activité dans le courant des activités obtiendrez une pause devant le nouveau est créé. La même règle s'applique lors de la finition (à l'aide de bouton "retour") à une activité.

J'ai trouvé deux méthodes qui semblent fonctionner comme voulu.

La première nécessite la GET_TASKS autorisation et se compose d'une méthode simple qui vérifie si les haut-activité en cours sur le périphérique appartient à l'application, en comparant les noms de package:

private boolean isApplicationBroughtToBackground() {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(context.getPackageName())) {
            return true;
        }
    }

    return false;
}

Cette méthode a été trouvé dans les Droid-Fu (maintenant appelé l'Allumage).

La deuxième méthode que j'ai mis en place mon auto ne nécessitent pas la GET_TASKS autorisation, ce qui est bon. Au lieu de cela, il est un peu plus compliqué à mettre en œuvre.

En vous MainApplication classe, vous disposez d'une variable qui mesure le nombre d'activités en cours d'exécution dans votre application. Dans onResume() pour chaque activité, vous augmentez la variable, et en onPause() vous diminuer.

Lorsque le nombre d'activités en cours d'exécution atteint 0, l'application est en arrière-plan SI les conditions suivantes sont remplies:

  • L'activité étant en pause n'est pas fini (bouton"retour" a été utilisé). Cela peut être fait en utilisant la méthode de l'activité.isFinishing()
  • Une nouvelle activité (même nom de paquet) n'est pas démarré. Vous pouvez remplacer le startActivity() méthode pour définir une variable qui indique que cela et puis de le réinitialiser en onPostResume(), qui est la dernière méthode à exécuter lorsqu'une activité est créé ou repris.

Lorsque vous pouvez détecter que l'application a démissionné de son poste à l'arrière-plan, il est facile de détecter quand il est ramené au premier plan.

Prograide.com

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

Powered by:

X