J'essaie d'écrire une application qui fait quelque chose de spécifique quand elle revient au premier plan après un certain temps. Existe-t-il un moyen de détecter le moment où une application est envoyée en arrière-plan ou au premier plan?
Les méthodes onPause()
et onResume()
sont appelées lorsque l'application est ramenée à l'arrière-plan et à l'avant-plan. Cependant, ils sont également appelés lorsque l'application est lancée pour la première fois et avant qu'elle ne soit tuée. Vous pouvez en lire plus dans Activité.
Il n’existe pas d’approche directe pour obtenir le statut de l’application en arrière-plan ou au premier plan, mais même j’ai fait face à ce problème et trouvé la solution avec onWindowFocusChanged
et onStop
.
Pour plus de détails, cliquez ici Android: solution pour détecter le moment où une application Android passe en arrière-plan et revient au premier plan sans getRunningTasks ou getRunningAppProcesses.
MISE À JOUR DE Mars 2018 : Il existe maintenant une meilleure solution. Voir ProcessLifecycleOwner . Vous devrez utiliser les nouveaux composants d’architecture 1.1.0 (les plus récents à ce jour), mais c’est spécifiquement conçu à cet effet.
Un exemple simple est fourni dans cette réponse mais j’ai écrit un exemple d’application et un message de blog à ce sujet.
Depuis que j'ai écrit ceci en 2014, différentes solutions sont apparues. Certains travaillaient, certains pensaient fonctionner , mais ils avaient des défauts (y compris le mien!) Et nous, en tant que communauté (Android), avons appris à vivre avec les conséquences et avons écrit des solutions de contournement pour les utilisateurs. cas spéciaux.
Ne supposez jamais qu’un extrait de code est la solution que vous recherchez, c’est peu probable. mieux encore, essayez de comprendre ce qu’il fait et pourquoi il le fait.
La classe MemoryBoss
n’a jamais été utilisée par moi comme il est écrit ici, c’était juste un morceau de pseudo-code qui fonctionnait.
À moins qu’il n’y ait une raison valable de ne pas utiliser les nouveaux composants d’architecture (et qu’il en existe, en particulier si vous ciblez de très vieux apis), utilisez-les. Ils sont loin d'être parfaits, mais ComponentCallbacks2
non plus.
UPDATE/NOTES (November 2015) : Deux personnes ont fait des commentaires. Tout d'abord, >=
doit être utilisé à la place de ==
car la documentation indique que vous ne devrait pas vérifier les valeurs exactes . C'est bien dans la plupart des cas, mais gardez à l'esprit que si vous seulement vous souciez de faire quelque chose lorsque l'application est passée à l'arrière-plan , vous devrez aussi utiliser == et avec une autre solution (comme les rappels Activity Lifecycle), ou vous ne pourrez pas obtenir votre effet désiré. L'exemple (et cela m'est arrivé) est que si vous voulez verrouiller votre application avec un écran de mot de passe lorsqu'il passe en arrière-plan (comme 1Password si vous le connaissez bien) ), vous risquez de verrouiller accidentellement votre application si vous manquez de mémoire et que vous testez subitement >= TRIM_MEMORY
, car Android déclenchera un appel LOW MEMORY
qui est supérieur au vôtre. Alors faites attention à ce que vous testez.
De plus, certaines personnes ont demandé comment détecter votre retour.
Le moyen le plus simple auquel je puisse penser est expliqué ci-dessous, mais comme certaines personnes ne le connaissent pas, j’ajoute un pseudo-code ici. En supposant que vous ayez YourApplication
et les classes MemoryBoss
dans votre class BaseActivity extends Activity
(vous devrez en créer un si vous n'en avez pas).
@Override
protected void onStart() {
super.onStart();
if (mApplication.wasInBackground()) {
// HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
mApplication.setWasInBackground(false);
}
}
Je recommande onStart, car les dialogues peuvent mettre une activité en pause. Je parie donc que vous ne voulez pas que votre application pense "qu'elle est passée à l'arrière-plan" si tout ce que vous faisiez était d'afficher une boîte de dialogue en plein écran, mais votre kilométrage peut varier.
Et c'est tout. Le code du bloc if ne sera exécuté qu'une seule fois , même si vous passez à une autre activité, la nouvelle (qui contient également extends BaseActivity
) signalera wasInBackground
est false
pour qu'il n'exécute pas le code jusqu'à ce que onMemoryTrimmed
soit appelé et que le drapeau soit à nouveau défini sur true .
J'espère que ça t'as aidé.
UPDATE/NOTES (Avril 2015) : Avant de vous lancer dans la copie et le collage de ce code, notez que j'ai trouvé quelques cas où cela ne s'est peut-être pas passé. être fiable à 100% et doivent être combinés avec d'autres méthodes pour obtenir les meilleurs résultats. Il existe notamment deux instances connues dans lesquelles l'exécution du rappel onTrimMemory
n'est pas garantie:
Si votre téléphone verrouille l'écran alors que votre application est visible (par exemple, votre appareil se verrouille au bout de nn minutes), ce rappel n'est pas appelé (ou pas toujours) car l'écran verrouillé est juste au-dessus, mais votre application est toujours "en cours d'exécution" bien qu'elle soit couverte.
Si la mémoire de votre appareil est relativement faible (et soumise à des contraintes de mémoire), le système d'exploitation semble ignorer cet appel et passer directement à des niveaux plus critiques.
Désormais, en fonction de l'importance pour vous de savoir quand votre application est passée à l'arrière-plan, vous pouvez ou non avoir besoin d'étendre cette solution tout en gardant une trace du cycle de vie de l'activité et de tout le reste.
Il suffit de garder ce qui précède à l’esprit et d’avoir une bonne équipe d’assurance qualité;)
FIN DE MISE À JOUR
C'est peut-être tard, mais il existe une méthode fiable entre sandwich à la crème glacée (API 14) et ci-dessus .
Il s'avère que lorsque votre application n'a plus d'interface utilisateur visible, un rappel est déclenché. Le rappel, que vous pouvez implémenter dans une classe personnalisée, est appelé ComponentCallbacks2 (oui, avec deux). Ce rappel est uniquement disponible dans les API de niveau 14 (sandwich) et au-dessus.
Vous obtenez essentiellement un appel à la méthode:
public abstract void onTrimMemory (int level)
Le niveau est 20 ou plus spécifiquement
public static final int TRIM_MEMORY_UI_HIDDEN
Je teste cela et cela fonctionne toujours, car le niveau 20 est simplement une "suggestion" selon laquelle vous souhaiterez peut-être libérer des ressources car votre application n'est plus visible.
Pour citer les documents officiels:
Niveau pour onTrimMemory (int): le processus montrait une interface utilisateur et ne le fait plus. Les allocations importantes avec l'interface utilisateur doivent être libérées à ce stade pour permettre à la mémoire d'être mieux gérée.
Bien sûr, vous devez implémenter ceci pour faire ce qui est dit (purger la mémoire qui n’a pas été utilisée depuis un certain temps, effacer certaines collections inutilisées, etc. Les possibilités sont infinies (voir la documentation officielle pour d’autres possibles niveaux plus critiques ).
Mais ce qui est intéressant, c’est que le système d’exploitation vous dit: HEY, votre application est passée au second plan!
Ce qui est exactement ce que vous vouliez savoir en premier lieu.
Comment déterminez-vous quand vous êtes rentré?
C'est facile, je suis sûr que vous avez une "BaseActivity" afin que vous puissiez utiliser votre onResume () pour signaler le fait que vous êtes de retour. Parce que la seule fois où vous direz que vous n'êtes pas de retour, c'est quand vous recevez un appel à la méthode onTrimMemory
ci-dessus.
Ça marche. Vous n'obtenez pas de faux positifs. Si une activité reprend, vous êtes de retour dans 100% des cas. Si l'utilisateur revient à l'arrière, vous recevez un autre appel onTrimMemory()
.
Vous devez vous inscrire à vos activités (ou, mieux encore, à un cours personnalisé).
Le moyen le plus simple de garantir que vous recevez toujours cela est de créer une classe simple comme celle-ci:
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 implémentation d'application (, vous en avez un, DROIT? ), procédez comme suit:
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
à ce if
et implémenter ComponentCallbacks
(sans le 2) utilisé dans les éléments inférieurs à l'API 14. Ce rappel ne possède que la fonction onLowMemory()
. method et ne sont pas appelés lorsque vous accédez à l'arrière-plan , mais vous devez l'utiliser pour réduire la mémoire.
Maintenant, lancez votre application et appuyez sur la maison. Votre méthode onTrimMemory(final int level)
doit être appelée (indice: ajouter une journalisation).
La dernière étape consiste à annuler l'inscription du rappel. Le meilleur endroit est probablement la méthode onTerminate()
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, à moins que vous ne souhaitiez vraiment pas être enregistré, vous pouvez l'ignorer, car votre processus est en train de mourir au niveau du système d'exploitation.
Si vous décidez de vous désinscrire à un moment donné (par exemple, si vous fournissez un mécanisme d'arrêt pour que votre application nettoie et meure), vous pouvez procéder comme suit:
unregisterComponentCallbacks(mMemoryBoss);
Et c'est tout.
Voici comment j'ai réussi à résoudre ce problème. Cela fonctionne sur le principe que l'utilisation d'une référence de temps entre les transitions d'activité fournira très probablement la preuve suffisante qu'une application a été "en arrière-plan" ou non.
Tout d’abord, j’ai utilisé une instance Android.app.Application (appelons-la MyApplication) qui contient un Timer, une TimerTask, une constante représentant le nombre maximal de millisecondes que la transition d’une activité à une autre peut raisonnablement prendre. avec une valeur de 2s), et un booléen pour indiquer si l'application était "en 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 démarrer et arrêter le minuteur/la 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;
}
Le dernier élément de cette solution consiste à ajouter un appel à chacune de ces méthodes à partir des événements onResume () et onPause () de toutes les activités ou, de préférence, dans une activité de base à partir de laquelle toutes vos activités concrètes héritent:
@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();
}
Ainsi, dans le cas où l'utilisateur navigue simplement entre les activités de votre application, onPause () de l'activité qui démarre démarre le chronomètre, mais presque immédiatement la nouvelle activité en cours l'annule avant qu'il ne puisse atteindre le délai de transition maximal. Et ainsi wasInBackground serait false.
D'autre part, lorsqu'une activité passe au premier plan du lanceur, le réveil de l'appareil, la fin de l'appel téléphonique, etc., il est plus que probable que la tâche de minuterie exécutée avant cet événement et donc wasInBackground a été définie. to true.
Edit: les nouveaux composants de l'architecture apportaient quelque chose de prometteur: ProcessLifecycleOwner , voir réponse de @ vokilam
class YourApplication : Application() {
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(AppLifecycleTracker())
}
}
class AppLifecycleTracker : Application.ActivityLifecycleCallbacks {
private var numStarted = 0
override fun onActivityStarted(activity: Activity?) {
if (numStarted == 0) {
// app went to foreground
}
numStarted++
}
override fun onActivityStopped(activity: Activity?) {
numStarted--
if (numStarted == 0) {
// app went to background
}
}
}
Oui. Je sais qu'il est difficile de croire que cette solution simple fonctionne, car nous avons ici beaucoup de solutions étranges.
Mais il y a de l'espoir.
ProcessLifecycleOwner
semble également être une solution prometteuse.
ProcessLifecycleOwner enverra les événements
ON_START
,ON_RESUME
, lorsqu’une première activité passe par ces événements.ON_PAUSE
,ON_STOP
, les événements seront distribués avec un délai après la dernière activité passée. Ce délai est suffisamment long pour garantir queProcessLifecycleOwner
n'enverra aucun événement si les activités sont détruites et recréées en raison d'un changement de configuration.
Une implémentation peut être aussi simple que
public class AppLifecycleListener implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onMoveToForeground() {
// app moved to foreground
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onMoveToBackground() {
// app moved to background
}
}
// register observer
ProcessLifecycleOwner.get().getLifecycle().addObserver(new AppLifecycleListener());
Selon le code source, le délai actuel est 700ms
.
L'utilisation de cette fonctionnalité requiert également le dependencies
:
implementation "Android.Arch.lifecycle:extensions:1.1.1"
annotationProcessor "Android.Arch.lifecycle:compiler:1.1.1"
Basé sur la réponse de Martín Marconcinis (merci!), J'ai finalement trouvé une solution fiable (et très simple).
public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
private static final String TAG = ApplicationLifecycleHandler.class.getSimpleName();
private static boolean isInBackground = false;
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
if(isInBackground){
Log.d(TAG, "app went to foreground");
isInBackground = false;
}
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onConfigurationChanged(Configuration configuration) {
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int i) {
if(i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
Log.d(TAG, "app went to background");
isInBackground = true;
}
}
}
Ajoutez ensuite ceci à votre onCreate () de votre classe Application
public class MyApp extends Android.app.Application {
@Override
public void onCreate() {
super.onCreate();
ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
registerActivityLifecycleCallbacks(handler);
registerComponentCallbacks(handler);
}
}
Nous utilisons cette méthode. Cela semble trop simple pour fonctionner, mais cela a été bien testé dans notre application et fonctionne en fait étonnamment bien dans tous les cas, y compris le passage à l'écran d'accueil par le bouton "Accueil", par le bouton "Retour" ou après le verrouillage de l'écran. Essaie.
L'idée est que, au premier plan, Android commence toujours une nouvelle activité juste avant d'arrêter la précédente. Ce n'est pas garanti, mais c'est comme ça que ça marche. BTW, Flurry semble utiliser la même logique (juste une supposition, je n'ai pas vérifié cela, mais cela dépend des mêmes événements).
public abstract class BaseActivity extends Activity {
private static int sessionDepth = 0;
@Override
protected void onStart() {
super.onStart();
sessionDepth++;
if(sessionDepth == 1){
//app came to foreground;
}
}
@Override
protected void onStop() {
super.onStop();
if (sessionDepth > 0)
sessionDepth--;
if (sessionDepth == 0) {
// app went to background
}
}
}
Edit: selon les commentaires, nous avons également migré vers onStart () dans les versions ultérieures du code. En outre, j'ajoute de super appels, qui étaient absents de mon message initial, car il s'agissait davantage d'un concept. code de travail.
Si votre application comporte plusieurs activités et/ou activités empilées, telles qu'un widget de barre de tabulation, le remplacement de onPause () et de onResume () ne fonctionnera pas. Ie lors du démarrage d'une nouvelle activité, les activités en cours seront mises en pause avant la création de la nouvelle. Il en va de même lorsque vous terminez (en utilisant le bouton "Précédent") une activité.
J'ai trouvé deux méthodes qui semblent fonctionner comme souhaité.
La première nécessite l'autorisation GET_TASKS et consiste en une méthode simple qui vérifie si l'activité en cours d'exécution sur le périphérique appartient à l'application, en comparant les noms de packages:
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ée dans le framework Droid-Fu (maintenant appelé Ignition).
La deuxième méthode que j'ai implémentée moi-même ne nécessite pas l'autorisation GET_TASKS, ce qui est bien. Au lieu de cela, il est un peu plus compliqué à mettre en œuvre.
Dans votre classe MainApplication, vous avez une variable qui suit le nombre d’activités en cours dans votre application. Dans onResume () pour chaque activité, vous augmentez la variable et dans onPause (), vous la diminuez.
Lorsque le nombre d'activités en cours atteint 0, l'application est mise en arrière-plan SI les conditions suivantes sont remplies:
Lorsque vous pouvez détecter que l'application s'est résignée à l'arrière-plan, il est également facile de détecter son retour au premier plan.
Créez une classe qui étend Application
. Ensuite, nous pouvons utiliser sa méthode de substitution, onTrimMemory()
.
Pour détecter si l'application est passée en tâche de fond, nous allons utiliser:
@Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
// Get called every-time when application went to background.
}
else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
}
}
Pensez à utiliser onUserLeaveHint. Ce n'est appelé que lorsque votre application passe en arrière-plan. onPause devra traiter les cas critiques, car il peut être appelé pour d'autres raisons; Par exemple, si l'utilisateur ouvre une autre activité dans votre application, telle que votre page de paramètres, la méthode onPause de votre activité principale sera appelée même si elle est toujours dans votre application. le suivi de ce qui se passe entraînera des bogues si vous pouvez simplement utiliser le rappel onUserLeaveHint qui fait ce que vous demandez.
Lorsque UserLeaveHint est appelé, vous pouvez définir un indicateur booléen inBackground sur true. Lorsque onResume est appelé, supposez que vous êtes revenu au premier plan si le drapeau inBackground est défini. En effet, onResume sera également appelé pour votre activité principale si l'utilisateur était simplement dans votre menu de paramètres et ne quittait jamais l'application.
Rappelez-vous que si l'utilisateur appuie sur le bouton d'accueil alors qu'il se trouvait dans l'écran de configuration, onUserLeaveHint sera appelé dans l'activité de configuration et lorsqu'il reviendra sur onResume sera appelé dans l'activité de configuration. Si vous ne disposez que de ce code de détection dans votre activité principale, vous manquerez ce cas d'utilisation. Pour avoir ce code dans toutes vos activités sans dupliquer le code, utilisez une classe d'activité abstraite qui étend l'activité et insérez votre code commun. Ensuite, chaque activité que vous avez peut prolonger cette activité abstraite.
Par exemple:
public abstract AbstractActivity extends Activity {
private static boolean inBackground = false;
@Override
public void onResume() {
if (inBackground) {
// You just came from the background
inBackground = false;
}
else {
// You just returned from another activity within your own app
}
}
@Override
public void onUserLeaveHint() {
inBackground = true;
}
}
public abstract MainActivity extends AbstractActivity {
...
}
public abstract SettingsActivity extends AbstractActivity {
...
}
ActivityLifecycleCallbacks pourrait être intéressant, mais ce n'est pas bien documenté.
Cependant, si vous appelez registerActivityLifecycleCallbacks (), vous devriez pouvoir obtenir des rappels lorsque les activités sont créées, détruites, etc. Vous pouvez appeler getComponentName () pour l'activité.
Le package Android.Arch.lifecycle fournit des classes et des interfaces vous permettant de créer des composants sensibles au cycle de vie.
Votre application doit implémenter l'interface LifecycleObserver:
public class MyApplication extends Application implements LifecycleObserver {
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private void onAppBackgrounded() {
Log.d("MyApp", "App in background");
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private void onAppForegrounded() {
Log.d("MyApp", "App in foreground");
}
}
Pour ce faire, vous devez ajouter cette dépendance à votre fichier build.gradle:
dependencies {
implementation "Android.Arch.lifecycle:extensions:1.1.1"
}
Conformément aux recommandations de Google, vous devez minimiser le code exécuté dans les méthodes d'activités du cycle de vie:
Un modèle courant consiste à implémenter les actions des composants dépendants dans les méthodes de cycle de vie des activités et des fragments. Cependant, cette tendance conduit à une mauvaise organisation du code et à la multiplication des erreurs. En utilisant des composants sensibles au cycle de vie, vous pouvez déplacer le code des composants dépendants des méthodes du cycle de vie et les insérer dans les composants eux-mêmes.
Vous pouvez en lire plus ici: https://developer.Android.com/topic/libraries/architecture/lifecycle
Dans votre application, ajoutez le rappel et vérifiez l'activité de la racine de la manière suivante:
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
loadDefaults();
}
}
});
}
J'ai créé un projet sur Github app-foreground-background-listen
Créez une activité de base pour toutes les activités de votre application.
public class BaseActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
public static boolean isAppInFg = false;
public static boolean isScrInFg = false;
public static boolean isChangeScrFg = false;
@Override
protected void onStart() {
if (!isAppInFg) {
isAppInFg = true;
isChangeScrFg = false;
onAppStart();
}
else {
isChangeScrFg = true;
}
isScrInFg = true;
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
if (!isScrInFg || !isChangeScrFg) {
isAppInFg = false;
onAppPause();
}
isScrInFg = false;
}
public void onAppStart() {
// Remove this toast
Toast.makeText(getApplicationContext(), "App in foreground", Toast.LENGTH_LONG).show();
// Your code
}
public void onAppPause() {
// Remove this toast
Toast.makeText(getApplicationContext(), "App in background", Toast.LENGTH_LONG).show();
// Your code
}
}
Maintenant, utilisez cette BaseActivity comme une super classe de toute votre activité, comme MainActivity étend BaseActivity et onAppStart sera appelé lorsque vous démarrez votre application et onAppPause () sera appelé lorsque l'application affichera l'arrière-plan à partir de n'importe quel écran.
C'est assez facile avec ProcessLifecycleOwner
Ajoutez ces dépendances
implementation "Android.Arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "Android.Arch.lifecycle:compiler:$project.archLifecycleVersion"
En Kotlin :
class ForegroundBackgroundListener : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startSomething() {
Log.v("ProcessLog", "APP IS ON FOREGROUND")
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopSomething() {
Log.v("ProcessLog", "APP IS IN BACKGROUND")
}
}
Puis dans votre activité de base:
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get()
.lifecycle
.addObserver(
ForegroundBackgroundListener()
.also { appObserver = it })
}
Voir mon article sur ce sujet: https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-Android-application-without-wanting -9719cc822c48
Il n'y a pas de méthode de cycle de vie simple pour vous dire quand toute l'application passe en arrière-plan/au premier plan.
J'ai fait cela d'une manière simple. Suivez les instructions ci-dessous pour détecter l’arrière-plan/la phase d’avant-plan de l’application.
Avec un peu de solution de contournement, c'est possible. Ici, ActivityLifecycleCallbacks vient à la rescousse. Laissez-moi parcourir pas à pas.
Commencez par créer une classe qui étend l’interface Android.app.Application et implémente l’interface ActivityLifecycleCallbacks. Dans Application.onCreate (), enregistrez le rappel.
public class App extends Application implements
Application.ActivityLifecycleCallbacks {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
}
Enregistrez la classe "App" dans le manifeste comme suit: <application Android:name=".App"
.
Il y aura au moins une activité dans l'état démarré lorsque l'application sera au premier plan et il n'y aura pas d'activité dans l'état démarré lorsque l'application sera en arrière-plan.
Déclarez 2 variables comme ci-dessous dans la classe “App”.
private int activityReferences = 0;
private boolean isActivityChangingConfigurations = false;
activityReferences
conservera le nombre de tâches dans l'état commencé. isActivityChangingConfigurations
est un indicateur pour indiquer si l'activité actuelle subit un changement de configuration, comme un commutateur d'orientation.
En utilisant le code suivant, vous pouvez détecter si l’application est mise au premier plan.
@Override
public void onActivityStarted(Activity activity) {
if (++activityReferences == 1 && !isActivityChangingConfigurations) {
// App enters foreground
}
}
Voici comment détecter si l'application passe en arrière-plan.
@Override
public void onActivityStopped(Activity activity) {
isActivityChangingConfigurations = activity.isChangingConfigurations();
if (--activityReferences == 0 && !isActivityChangingConfigurations) {
// App enters background
}
}
Comment ça marche:
C'est une petite astuce réalisée avec la façon dont les méthodes Lifecycle sont appelées en séquence. Laissez-moi parcourir un scénario.
Supposons que l'utilisateur lance l'application et que l'activité de lancement A soit lancée. Les appels du cycle de vie seront,
A.onCreate ()
A.onStart () (++ activityReferences == 1) (l'application entre au premier plan)
A.onResume ()
Maintenant, l'activité A commence l'activité B.
A.onPause ()
B.onCreate ()
B.onStart () (++ activityReferences == 2)
B.onResume ()
A.onStop () (--activityReferences == 1)
Ensuite, l'utilisateur revient de l'activité B,
B.onPause ()
A.onStart () (++ activityReferences == 2)
A.onResume ()
B.onStop () (--activityReferences == 1)
B.onDestroy ()
Ensuite, l'utilisateur appuie sur le bouton Accueil,
A.onPause ()
A.onStop () (--activityReferences == 0) (l'application entre en arrière-plan)
Dans le cas où l'utilisateur appuie sur le bouton d'accueil de l'activité B au lieu du bouton de retour, il en sera toujours de même et activitéRéférences sera 0
. Par conséquent, nous pouvons détecter que l’application entre en arrière-plan.
Alors, quel est le rôle de isActivityChangingConfigurations
? Dans le scénario ci-dessus, supposons que l'activité B change l'orientation. La séquence de rappel sera,
B.onPause ()
B.onStop () (--activityReferences == 0) (l'application entre en arrière-plan ??)
B.onDestroy ()
B.onCreate ()
B.onStart () (++ activityReferences == 1) (l'application entre au premier plan ??)
B.onResume ()
C’est pourquoi nous avons effectué une vérification supplémentaire de isActivityChangingConfigurations
afin d’éviter le scénario où l’Activité subit les modifications de configuration.
J'ai trouvé une bonne méthode pour détecter une application, que ce soit en avant-plan ou en arrière-plan. Voici mon code . J'espère que cela vous aidera.
/**
* Custom Application which can detect application state of whether it enter
* background or enter foreground.
*
* @reference http://www.vardhan-justlikethat.blogspot.sg/2014/02/Android-solution-to-detect-when-Android.html
*/
public abstract class StatusApplication extends Application implements ActivityLifecycleCallbacks {
public static final int STATE_UNKNOWN = 0x00;
public static final int STATE_CREATED = 0x01;
public static final int STATE_STARTED = 0x02;
public static final int STATE_RESUMED = 0x03;
public static final int STATE_PAUSED = 0x04;
public static final int STATE_STOPPED = 0x05;
public static final int STATE_DESTROYED = 0x06;
private static final int FLAG_STATE_FOREGROUND = -1;
private static final int FLAG_STATE_BACKGROUND = -2;
private int mCurrentState = STATE_UNKNOWN;
private int mStateFlag = FLAG_STATE_BACKGROUND;
@Override
public void onCreate() {
super.onCreate();
mCurrentState = STATE_UNKNOWN;
registerActivityLifecycleCallbacks(this);
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
// mCurrentState = STATE_CREATED;
}
@Override
public void onActivityStarted(Activity activity) {
if (mCurrentState == STATE_UNKNOWN || mCurrentState == STATE_STOPPED) {
if (mStateFlag == FLAG_STATE_BACKGROUND) {
applicationWillEnterForeground();
mStateFlag = FLAG_STATE_FOREGROUND;
}
}
mCurrentState = STATE_STARTED;
}
@Override
public void onActivityResumed(Activity activity) {
mCurrentState = STATE_RESUMED;
}
@Override
public void onActivityPaused(Activity activity) {
mCurrentState = STATE_PAUSED;
}
@Override
public void onActivityStopped(Activity activity) {
mCurrentState = STATE_STOPPED;
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
mCurrentState = STATE_DESTROYED;
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (mCurrentState == STATE_STOPPED && level >= TRIM_MEMORY_UI_HIDDEN) {
if (mStateFlag == FLAG_STATE_FOREGROUND) {
applicationDidEnterBackground();
mStateFlag = FLAG_STATE_BACKGROUND;
}
}else if (mCurrentState == STATE_DESTROYED && level >= TRIM_MEMORY_UI_HIDDEN) {
if (mStateFlag == FLAG_STATE_FOREGROUND) {
applicationDidDestroyed();
mStateFlag = FLAG_STATE_BACKGROUND;
}
}
}
/**
* The method be called when the application been destroyed. But when the
* device screen off,this method will not invoked.
*/
protected abstract void applicationDidDestroyed();
/**
* The method be called when the application enter background. But when the
* device screen off,this method will not invoked.
*/
protected abstract void applicationDidEnterBackground();
/**
* The method be called when the application enter foreground.
*/
protected abstract void applicationWillEnterForeground();
}
Edit 2: Ce que j'ai écrit ci-dessous ne fonctionnera pas réellement. Google a rejeté une application qui inclut un appel à ActivityManager.getRunningTasks (). D'après la documentation , il est évident que cette API est uniquement destinée au débogage et au développement. Je mettrai à jour ce post dès que j'aurai le temps de mettre à jour le projet GitHub ci-dessous avec un nouveau schéma qui utilise des minuteries et qui est presque aussi bon.
Edit 1: J'ai écrit un article de blog et créé n simple référentiel GitHub pour que cela soit vraiment facile.
La réponse acceptée et la meilleure cotée ne sont pas vraiment la meilleure approche. L'implémentation de isApplicationBroughtToBackground () dans la réponse la mieux notée ne gère pas la situation dans laquelle l'activité principale de l'application cède la place à une activité définie dans la même application, mais comporte un package Java différent. Je suis venu avec un moyen de faire cela qui fonctionnera dans ce cas.
Appelez ceci dans onPause (), et il vous dira si votre application passe en arrière-plan, car une autre application a démarré ou si l'utilisateur a appuyé sur le bouton d'accueil.
public static boolean isApplicationBroughtToBackground(final Activity activity) {
ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);
// Check the top Activity against the list of Activities contained in the Application's package.
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
try {
PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
for (ActivityInfo activityInfo : pi.activities) {
if(topActivity.getClassName().equals(activityInfo.name)) {
return false;
}
}
} catch( PackageManager.NameNotFoundException e) {
return false; // Never happens.
}
}
return true;
}
Réponse correcte ici
Créez une classe avec le nom MyApp comme ci-dessous:
public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
private Context context;
public void setContext(Context context)
{
this.context = context;
}
private boolean isInBackground = false;
@Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
isInBackground = true;
Log.d("status = ","we are out");
}
}
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
if(isInBackground){
isInBackground = false;
Log.d("status = ","we are in");
}
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onConfigurationChanged(Configuration configuration) {
}
@Override
public void onLowMemory() {
}
}
Ensuite, partout où vous le souhaitez (meilleure première activité lancée dans l'application), ajoutez le code ci-dessous:
MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);
Terminé! Maintenant, lorsque l'application est en arrière-plan, nous obtenons journal status : we are out
et lorsque nous allons dans l'application, nous obtenons journal status : we are out
Voici la version modifiée de la réponse de @ d60402: https://stackoverflow.com/a/15573121/4747587
Faites tout ce qui est mentionné ici. Mais au lieu d’avoir un Base Activity
et d’en faire un parent pour chaque activité et de remplacer la onResume()
et onPause
, procédez comme suit:
Dans votre classe d'application, ajoutez la ligne:
registerActivityLifecycleCallbacks (rappel Application.ActivityLifecycleCallbacks);
Cette callback
a toutes les méthodes de cycle de vie d'activité et vous pouvez maintenant remplacer onActivityResumed()
et onActivityPaused()
.
Jetez un oeil à cet article: https://Gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b
Vous pouvez utiliser ProcessLifecycleOwner pour y attacher un observateur de cycle de vie.
public class ForegroundLifecycleObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void onAppCreated() {
Timber.d("onAppCreated() called");
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppStarted() {
Timber.d("onAppStarted() called");
}
@OnLifecycleEvent(Event.ON_RESUME)
public void onAppResumed() {
Timber.d("onAppResumed() called");
}
@OnLifecycleEvent(Event.ON_PAUSE)
public void onAppPaused() {
Timber.d("onAppPaused() called");
}
@OnLifecycleEvent(Event.ON_STOP)
public void onAppStopped() {
Timber.d("onAppStopped() called");
}
}
ensuite, sur la onCreate()
de votre classe Application, appelez ceci:
ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());
avec cela, vous pourrez capturer les événements de ON_PAUSE
et ON_STOP
de votre application qui se produisent quand elle passe en arrière-plan.
J'utilisais cela avec Google Analytics EasyTracker, et cela fonctionnait. Il pourrait être étendu pour faire ce que vous cherchez en utilisant un entier simple.
public class MainApplication extends Application {
int isAppBackgrounded = 0;
@Override
public void onCreate() {
super.onCreate();
appBackgroundedDetector();
}
private void appBackgroundedDetector() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
EasyTracker.getInstance(MainApplication.this).activityStart(activity);
}
@Override
public void onActivityResumed(Activity activity) {
isAppBackgrounded++;
if (isAppBackgrounded > 0) {
// Do something here
}
}
@Override
public void onActivityPaused(Activity activity) {
isAppBackgrounded--;
}
@Override
public void onActivityStopped(Activity activity) {
EasyTracker.getInstance(MainApplication.this).activityStop(activity);
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
je sais que c'est un peu tard, mais je pense que toutes ces réponses ont quelques problèmes alors que je l'ai fait comme ci-dessous et cela fonctionne parfaitement.
créer un rappel de cycle de vie d'activité comme ceci:
class ActivityLifeCycle implements ActivityLifecycleCallbacks{
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
}
Activity lastActivity;
@Override
public void onActivityResumed(Activity activity) {
//if (null == lastActivity || (activity != null && activity == lastActivity)) //use this condition instead if you want to be informed also when app has been killed or started for the first time
if (activity != null && activity == lastActivity)
{
Toast.makeText(MyApp.this, "NOW!", Toast.LENGTH_LONG).show();
}
lastActivity = activity;
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}
et enregistrez-le simplement sur votre classe d'application comme ci-dessous:
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}
Cela semble être l’une des questions les plus complexes de Android puisque (au moment de l'écriture de cette lettre), Android n'a pas d'équivalent iOS de applicationDidEnterBackground()
ou applicationWillEnterForeground()
. rappels. J'ai utilisé un AppState Library qui a été mis en place par @ jenzz .
[AppState est] une bibliothèque Android simple et réactive, basée sur RxJava, qui surveille les changements d'état de l'application. Il informe les abonnés chaque fois que l'application passe en arrière-plan et revient au premier plan.
Il s’est avéré que c’était exactement ce dont j'avais besoin, en particulier parce que mon application comportait plusieurs activités. Il était donc simple de vérifier onStart()
ou onStop()
sur une activité.
J'ai d'abord ajouté ces dépendances à gradle:
dependencies {
compile 'com.jenzz.appstate:appstate:3.0.1'
compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}
Ensuite, il s’est agi simplement d’ajouter ces lignes à un emplacement approprié dans votre code:
//Note that this uses RxJava 2.x adapter. Check the referenced github site for other ways of using observable
Observable<AppState> appState = RxAppStateMonitor.monitor(myApplication);
//where myApplication is a subclass of Android.app.Application
appState.subscribe(new Consumer<AppState>() {
@Override
public void accept(@io.reactivex.annotations.NonNull AppState appState) throws Exception {
switch (appState) {
case FOREGROUND:
Log.i("info","App entered foreground");
break;
case BACKGROUND:
Log.i("info","App entered background");
break;
}
}
});
En fonction de votre abonnement à l'observable, vous devrez peut-être vous désabonner pour éviter les fuites de mémoire. Encore une fois plus d'informations sur le page github .
Étant donné que je n'ai trouvé aucune approche, qui gère également la rotation sans vérifier les horodatages, j'ai également partagé comment nous procédons maintenant dans notre application. Le seul ajout à cette réponse https://stackoverflow.com/a/42679191/5119746 est que nous prenons également en compte l'orientation.
class MyApplication : Application(), Application.ActivityLifecycleCallbacks {
// Members
private var mAppIsInBackground = false
private var mCurrentOrientation: Int? = null
private var mOrientationWasChanged = false
private var mResumed = 0
private var mPaused = 0
Ensuite, pour les rappels, nous avons d'abord le CV:
// ActivityLifecycleCallbacks
override fun onActivityResumed(activity: Activity?) {
mResumed++
if (mAppIsInBackground) {
// !!! App came from background !!! Insert code
mAppIsInBackground = false
}
mOrientationWasChanged = false
}
Et onActivityStopped:
override fun onActivityStopped(activity: Activity?) {
if (mResumed == mPaused && !mOrientationWasChanged) {
// !!! App moved to background !!! Insert code
mAppIsInBackground = true
}
Et puis, voici l'addition: Vérification des changements d'orientation:
override fun onConfigurationChanged(newConfig: Configuration) {
if (newConfig.orientation != mCurrentOrientation) {
mCurrentOrientation = newConfig.orientation
mOrientationWasChanged = true
}
super.onConfigurationChanged(newConfig)
}
C'est ça. J'espère que ça aide quelqu'un :)
Ma solution a été inspirée par la réponse de @ d60402 et repose également sur une fenêtre temporelle, mais n'utilise pas la variable Timer
:
public abstract class BaseActivity extends ActionBarActivity {
protected boolean wasInBackground = false;
@Override
protected void onStart() {
super.onStart();
wasInBackground = getApp().isInBackground;
getApp().isInBackground = false;
getApp().lastForegroundTransition = System.currentTimeMillis();
}
@Override
protected void onStop() {
super.onStop();
if( 1500 < System.currentTimeMillis() - getApp().lastForegroundTransition )
getApp().isInBackground = true;
}
protected SingletonApplication getApp(){
return (SingletonApplication)getApplication();
}
}
où SingletonApplication
est une extension de Application
classe:
public class SingletonApplication extends Application {
public boolean isInBackground = false;
public long lastForegroundTransition = 0;
}
Vous pouvez y parvenir facilement à l'aide de ActivityLifecycleCallbacks
et ComponentCallbacks2
comme ci-dessous.
Créez une classe AppLifeCycleHandler
implémentant les interfaces susmentionnées.
package com.sample.app;
import Android.app.Activity;
import Android.app.Application;
import Android.content.ComponentCallbacks2;
import Android.content.res.Configuration;
import Android.os.Bundle;
/**
* Created by Naveen on 17/04/18
*/
public class AppLifeCycleHandler
implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
AppLifeCycleCallback appLifeCycleCallback;
boolean appInForeground;
public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
this.appLifeCycleCallback = appLifeCycleCallback;
}
@Override
public void onActivityResumed(Activity activity) {
if (!appInForeground) {
appInForeground = true;
appLifeCycleCallback.onAppForeground();
}
}
@Override
public void onTrimMemory(int i) {
if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
appInForeground = false;
appLifeCycleCallback.onAppBackground();
}
}
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onConfigurationChanged(Configuration configuration) {
}
@Override
public void onLowMemory() {
}
interface AppLifeCycleCallback {
void onAppBackground();
void onAppForeground();
}
}
Dans votre classe, qui étend Application
implémentez AppLifeCycleCallback
pour obtenir les rappels lorsque l'application bascule entre l'avant-plan et l'arrière-plan. Quelque chose comme ci-dessous.
public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{
@Override
public void onCreate() {
super.onCreate();
AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
registerActivityLifecycleCallbacks(appLifeCycleHandler);
registerComponentCallbacks(appLifeCycleHandler);
}
@Override
public void onAppBackground() {
Log.d("LifecycleEvent", "onAppBackground");
}
@Override
public void onAppForeground() {
Log.d("LifecycleEvent", "onAppForeground");
}
}
J'espère que cela t'aides.
EDIT Alternativement, vous pouvez maintenant utiliser le composant d'architecture sensible au cycle de vie.
C’est ma solution https://github.com/doridori/AndroidUtils/blob/master/App/src/main/Java/com/doridori/lib/app/ActivityCounter.Java
Fondamentalement impliqué dans le comptage des méthodes de cycle de vie pour toutes les activités avec une minuterie pour détecter les cas où aucune activité n'est actuellement au premier plan alors que l'application est (c'est-à-dire en rotation)
J'ai réussi à surveiller la navigation des applications en arrière-plan et en arrière-plan en implémentant une BaseActivity exploitant l'utilisation des rappels d'activité onResume, onPause et onStop. Voici mes implémentations.
override fun onResume() {
super.onResume()
if (AppActivityState.state == AppState.ON_LAUNCHED) {
// We are in the first launch.
onLaunched()
} else {
if (AppActivityState.state == AppState.ON_BACKGROUND) {
// We came from background to foreground.
AppActivityState.state = AppState.ON_FOREGROUND
onForeground()
} else {
// We are just navigating through pages.
AppActivityState.state = AppState.RESUMED
}
}
}
override fun onPause() {
super.onPause()
// If state is followed by onStop then it means we will going to background.
AppActivityState.state = AppState.PAUSED
}
override fun onStop() {
super.onStop()
// App will go to background base on the 'pause' cue.
if (AppActivityState.state == AppState.PAUSED) {
AppActivityState.state = AppState.ON_BACKGROUND
onBackground()
}
}
Après avoir créé BaseActivity, il vous suffit d’étendre cette activité à n’importe quelle activité de votre application.
Dans ce type d'implémentation, vous pouvez détecter avec précision les éléments suivants: - onBackground> l'application passe en arrière-plan - onForeground> l'application revient au premier plan - onLaunch> l'application vient d'ouvrir
J'espère que cela t'aidera :)
Voici ma solution. Enregistrez simplement ce ActivityLifecycleCallbacks dans votre classe d'application principale. Dans les commentaires, je mentionne un cas de profil d'activité de profil d'utilisateur. Cette activité est simplement une activité avec des bords transparents.
/**
* This class used Activity lifecycle callbacks to determine when the application goes to the
* background as well as when it is brought to the foreground.
*/
public class Foreground implements Application.ActivityLifecycleCallbacks
{
/**
* How long to wait before checking onStart()/onStop() count to determine if the app has been
* backgrounded.
*/
public static final long BACKGROUND_CHECK_DELAY_MS = 500;
private static Foreground sInstance;
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private boolean mIsForeground = false;
private int mCount;
public static void init(final Application application)
{
if (sInstance == null)
{
sInstance = new Foreground();
application.registerActivityLifecycleCallbacks(sInstance);
}
}
public static Foreground getInstance()
{
return sInstance;
}
public boolean isForeground()
{
return mIsForeground;
}
public boolean isBackground()
{
return !mIsForeground;
}
@Override
public void onActivityStarted(final Activity activity)
{
mCount++;
// Remove posted Runnables so any Meteor disconnect is cancelled if the user comes back to
// the app before it runs.
mMainThreadHandler.removeCallbacksAndMessages(null);
if (!mIsForeground)
{
mIsForeground = true;
}
}
@Override
public void onActivityStopped(final Activity activity)
{
mCount--;
// A transparent Activity like community user profile won't stop the Activity that launched
// it. If you launch another Activity from the user profile or hit the Android home button,
// there are two onStops(). One for the user profile and one for its parent. Remove any
// posted Runnables so we don't get two session ended events.
mMainThreadHandler.removeCallbacksAndMessages(null);
mMainThreadHandler.postDelayed(new Runnable()
{
@Override
public void run()
{
if (mCount == 0)
{
mIsForeground = false;
}
}
}, BACKGROUND_CHECK_DELAY_MS);
}
@Override
public void onActivityCreated(final Activity activity, final Bundle savedInstanceState)
{
}
@Override
public void onActivityResumed(final Activity activity)
{
}
@Override
public void onActivityPaused(final Activity activity)
{
}
@Override
public void onActivitySaveInstanceState(final Activity activity, final Bundle outState)
{
}
@Override
public void onActivityDestroyed(final Activity activity)
{
}
}
Nous pouvons développer cette solution en utilisant LiveData
:
class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {
private var lifecycleListener: LifecycleObserver? = null
override fun onActive() {
super.onActive()
lifecycleListener = AppLifecycleListener().also {
ProcessLifecycleOwner.get().lifecycle.addObserver(it)
}
}
override fun onInactive() {
super.onInactive()
lifecycleListener?.let {
this.lifecycleListener = null
ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
}
}
internal inner class AppLifecycleListener : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
value = State.FOREGROUND
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() {
value = State.BACKGROUND
}
}
enum class State {
FOREGROUND, BACKGROUND
}
}
Maintenant, nous pouvons nous abonner à ce LiveData et attraper les événements nécessaires. Par exemple:
appForegroundStateLiveData.observeForever { state ->
when(state) {
AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
}
}
Mon application doit "redémarrer" après le retour de l'arrière-plan - afficher une série d'activités, en fonction des sollicitations des clients. Après une recherche approfondie sur la façon de gérer les transitions d’arrière-plan/premier plan (traité très différemment entre iOS et Android), j’ai traversé cette question. Vous avez trouvé une aide très utile ici, spécialement de la réponse la plus votée et celle signalée comme correcte. Cependant, il suffit de rétablir l’activité racine CHAQUE FOIS que l’application entre au premier plan a l’air trop gênante lorsque l’on pense à UX. La solution qui a fonctionné pour moi, et celle qui me semble la plus adaptée - basée sur les fonctionnalités des applications Youtube et Twitter - consistait à combiner les réponses de @GirishNair à @ d60402: Appel du minuteur lorsque la mémoire de rognage de l'application était sélectionnée:
@Override
public void onTrimMemory(int level) {
if (stateOfLifeCycle.equals("Stop")) {
startActivityTransitionTimer();
}
super.onTrimMemory(level);
}
Ma limite de minuterie est fixée à 30 secondes - je songe à l'augmenter un peu.
private final long MAX_ACTIVITY_TRANSITION_TIME = 30000;
Et lorsque l'application passe au premier plan, est relancée ou qu'elle est détruite, appelez la méthode pour annuler le chronomètre.
Sur l'extension de l'application:
@Override
public void onActivityCreated(Activity activity, Bundle arg1) {
stopActivityTransitionTimer();
stateOfLifeCycle = "Create";
}
@Override
public void onActivityDestroyed(Activity activity) {
stopActivityTransitionTimer();
stateOfLifeCycle = "Destroy";
}
Sur l'activité (de préférence sur une activité de base, héritée des autres):
@Override
protected void onStart() {
super.onStart();
if (App.wasInBackground) {
stopActivityTransitionTimer();
}
}
Dans mon cas, lorsque l'application passe au premier plan après le délai maximal, une nouvelle tâche est créée. Par conséquent, la fonction stopActivityTransitionTimer () est appelée onActivityCreated () ou onActivityDestroyed (), dans la classe d'extension de l'application, ce qui rend inutile d'appeler la méthode dans une activité. . J'espère que ça aide.
Que diriez-vous de cette solution
public class BaseActivity extends Activity
{
static String currentAct = "";
@Override
protected void onStart()
{
super.onStart();
if (currentAct.equals(""))
Toast.makeText(this, "Start", Toast.LENGTH_LONG).show();
currentAct = getLocalClassName();
}
@Override
protected void onStop()
{
super.onStop();
if (currentAct.equals(getLocalClassName()))
{
currentAct = "";
Toast.makeText(this, "Stop", Toast.LENGTH_LONG).show();
}
}
}
Toute activité doit étendre BaseActivity.
Quand une activité appelle une autre (A-> B) alors currentAct n’est pas égal à getLocalClassName () car onStart () de la deuxième activité (B) est appelé avant le onStop () de la première (A) ( https : //developer.Android.com/guide/components/activities.html#CoordinatingActivities ).
Lorsque l'utilisateur appuie sur le bouton d'accueil ou que vous passez d'une application à l'autre, il suffit d'appeler onStop (), puis currentAct est égal à getLocalClassName ().
En utilisant le code ci-dessous, je peux obtenir l'état de mon avant-plan ou de mon arrière-plan de l'application.
Pour plus de détails sur son fonctionnement, cliquez sur le texte fort ici
import Android.content.ComponentCallbacks2;
import Android.content.Context;
import Android.support.v7.app.AppCompatActivity;
import Android.os.Bundle;
import Android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private Context context;
private Toast toast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
}
private void showToast(String message) {
//If toast is already showing cancel it
if (toast != null) {
toast.cancel();
}
toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
toast.show();
}
@Override
protected void onStart() {
super.onStart();
showToast("App In Foreground");
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
showToast("App In Background");
}
}
}
Ce que j'ai fait est de m'assurer que toutes les activités intégrées à l'application sont lancées avec startActivityForResult
, puis de vérifier si onActivityResult a été appelé avant onResume. Si ce n'était pas le cas, cela signifie que nous venons de rentrer de quelque part en dehors de notre application.
boolean onActivityResultCalledBeforeOnResume;
@Override
public void startActivity(Intent intent) {
startActivityForResult(intent, 0);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
onActivityResultCalledBeforeOnResume = true;
}
@Override
protected void onResume() {
super.onResume();
if (!onActivityResultCalledBeforeOnResume) {
// here, app was brought to foreground
}
onActivityResultCalledBeforeOnResume = false;
}
Ces réponses ne semblent pas être correctes. Ces méthodes sont également appelées lorsqu'une autre activité commence et se termine. Ce que vous pouvez faire est de garder un drapeau global (oui, les globales sont mauvaises :) et définissez-le sur true chaque fois que vous démarrez une nouvelle activité. Définissez-le sur false dans la création de chaque activité. Ensuite, dans le onPause, cochez cette case. Si elle est fausse, votre application passe en arrière-plan ou se fait tuer.