it-swarm.com.de

So erkennen Sie, wann eine Android App in den Hintergrund tritt und wieder in den Vordergrund tritt

Ich versuche, eine App zu schreiben, die etwas Bestimmtes tut, wenn sie nach einiger Zeit wieder in den Vordergrund gerückt wird. Gibt es eine Möglichkeit zu erkennen, wann eine App in den Hintergrund oder in den Vordergrund gestellt wird?

357
iHorse

Die Methoden onPause() und onResume() werden aufgerufen, wenn die Anwendung wieder in den Hintergrund und in den Vordergrund gebracht wird. Sie werden jedoch auch aufgerufen, wenn die Anwendung zum ersten Mal gestartet wird und bevor sie beendet wird. Sie können mehr in Aktivität lesen.

Es gibt keine direkte Möglichkeit, den Anwendungsstatus im Hintergrund oder im Vordergrund abzurufen, aber selbst ich habe mich mit diesem Problem befasst und die Lösung mit onWindowFocusChanged und onStop gefunden.

Weitere Informationen finden Sie hier Android: Lösung, um zu erkennen, wann eine Android App in den Hintergrund wechselt und ohne getRunningTasks oder getRunningAppProcesses in den Vordergrund zurückkehrt.

88
Swind

2018: Android unterstützt dies nativ durch Lebenszykluskomponenten.

März 2018 UPDATE : Es gibt jetzt eine bessere Lösung. Siehe ProcessLifecycleOwner . Sie müssen die neuen Architekturkomponenten 1.1.0 (spätestens zu diesem Zeitpunkt) verwenden, aber es wurde spezifisch dafür entwickelt.

Es gibt ein einfaches Beispiel in dieser Antwort , aber ich habe ein Beispiel-App und ein Blog-Beitrag darüber geschrieben.

Seit ich das 2014 geschrieben habe, sind verschiedene Lösungen entstanden. Einige funktionierten, andere waren arbeiteten vermutlich , hatten aber Mängel (einschließlich meiner!) Und wir als Community (Android) lernten, mit den Folgen umzugehen und schrieben Workarounds für das Sonderfälle.

Nehmen Sie niemals an, dass ein einziger Codeausschnitt die Lösung ist, nach der Sie suchen. Es ist unwahrscheinlich, dass dies der Fall ist. Noch besser, versuchen Sie zu verstehen, was es tut und warum es es tut.

Die MemoryBoss -Klasse wurde von mir nie so verwendet, wie hier geschrieben, es war nur ein Teil des Pseudocodes, der zufällig funktioniert hat.

Es sei denn, es gibt einen triftigen Grund dafür, dass Sie die neuen Architekturkomponenten nicht verwenden (und es gibt einige, insbesondere, wenn Sie auf Super-Old-Apis abzielen), verwenden Sie diese. Sie sind alles andere als perfekt, aber sie waren auch nicht ComponentCallbacks2.

UPDATE/NOTES (November 2015) : Die Leute haben zwei Kommentare abgegeben. Erstens sollte >= anstelle von == verwendet werden, da in der Dokumentation angegeben ist, dass Sie sollte nicht nach genauen Werten suchen . Dies ist in den meisten Fällen in Ordnung, aber denken Sie daran, dass Sie, wenn Sie only möchten, something tun, wenn die App in den Hintergrund ging , müssen Sie == und auch mit einer anderen Lösung kombinieren (z. B. Activity Lifecycle-Rückrufe), oder Sie erhalten möglicherweise nicht Ihr gewünschter Effekt. Das Beispiel (und das ist mir passiert) ist, dass Sie sperren Ihre App mit einem Passwort-Bildschirm versehen möchten, wenn dieser im Hintergrund angezeigt wird (wie 1Password, wenn Sie damit vertraut sind ), können Sie Ihre App versehentlich sperren, wenn der Arbeitsspeicher knapp wird und Sie plötzlich auf >= TRIM_MEMORY testen, da Android einen LOW MEMORY-Aufruf auslöst und dieser höher ist als der Ihre. Also sei vorsichtig, wie/was du testest.

Darüber hinaus haben einige Leute gefragt, wie man erkennt, wann man zurückkommt.

Der einfachste Weg, den ich mir vorstellen kann, wird unten erklärt, aber da einige Leute nicht mit ihm vertraut sind, füge ich hier Pseudocode hinzu. Angenommen, Sie haben die Klassen YourApplication und MemoryBoss in Ihrem class BaseActivity extends Activity (Sie müssen eine erstellen, wenn Sie keine haben).

@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);
    }
}

Ich empfehle onStart, da Dialoge eine Aktivität anhalten können, damit Ihre App nicht den Eindruck erweckt, dass sie im Hintergrund angezeigt wird, wenn Sie lediglich einen Vollbild-Dialog angezeigt haben. Ihr Kilometerstand kann jedoch variieren.

Und das ist alles. Der Code im if-Block wird wird nur einmal ausgeführt , auch wenn Sie zu einer anderen Aktivität gehen, die neue (die auch extends BaseActivity) wird wasInBackground ist false Damit der Code nicht ausgeführt wird, bis onMemoryTrimmed aufgerufen wird und das Flag wieder auf true gesetzt wird .

Ich hoffe, das hilft.

UPDATE/NOTES (April 2015) : Bevor Sie diesen Code vollständig kopieren und einfügen, habe ich einige Fälle gefunden, in denen dies möglicherweise nicht der Fall ist 100% zuverlässig sein und muss mit anderen Methoden kombiniert werden, um die besten Ergebnisse zu erzielen. Insbesondere gibt es zwei bekannte Fälle , in denen die Ausführung des onTrimMemory -Rückrufs nicht garantiert ist:

  1. Wenn Ihr Telefon den Bildschirm sperrt, während Ihre App sichtbar ist (z. B. Ihr Gerät sperrt nach nn Minuten), wird dieser Rückruf nicht (oder nicht immer) aufgerufen, da der Sperrbildschirm nur oben angezeigt wird, Ihre App jedoch weiterhin "ausgeführt" wird, obwohl sie verdeckt ist.

  2. Wenn Ihr Gerät relativ wenig Speicher hat (und unter Speicherbelastung steht), scheint das Betriebssystem diesen Aufruf zu ignorieren und direkt zu kritischeren Ebenen überzugehen.

Abhängig davon, wie wichtig es ist, dass Sie wissen, wann Ihre App in den Hintergrund gerückt ist, müssen Sie diese Lösung möglicherweise erweitern oder auch nicht, indem Sie den Aktivitätslebenszyklus verfolgen und vieles mehr.

Denken Sie einfach an die oben genannten Punkte und haben Sie ein gutes QA-Team;)

ENDE DER AKTUALISIERUNG

Es mag spät sein, aber es gibt eine zuverlässige Methode in Ice Cream Sandwich (API 14) und darüber .

Es stellt sich heraus, dass ein Rückruf ausgelöst wird, wenn Ihre App keine sichtbare Benutzeroberfläche mehr hat. Der Rückruf, den Sie in einer benutzerdefinierten Klasse implementieren können, heißt ComponentCallbacks2 (ja, mit zwei). Dieser Rückruf ist nur in API Level 14 (Ice Cream Sandwich) und höher verfügbar .

Grundsätzlich erhalten Sie einen Aufruf der Methode:

public abstract void onTrimMemory (int level)

Die Stufe ist 20 oder genauer

public static final int TRIM_MEMORY_UI_HIDDEN

Ich habe dies getestet und es funktioniert immer, da Level 20 nur ein "Vorschlag" ist, dass Sie möglicherweise einige Ressourcen freigeben möchten, da Ihre App nicht mehr sichtbar ist.

So zitieren Sie die offiziellen Dokumente:

Stufe für onTrimMemory (int): Der Prozess hat eine Benutzeroberfläche angezeigt und tut dies nicht mehr. Zu diesem Zeitpunkt sollten umfangreiche Zuweisungen für die Benutzeroberfläche freigegeben werden, damit der Speicher besser verwaltet werden kann.

Natürlich sollten Sie dies implementieren, um tatsächlich das zu tun, was es sagt (Speicher löschen, der in einer bestimmten Zeit nicht verwendet wurde, einige Sammlungen löschen, die nicht verwendet wurden, usw.) kritischere Ebenen).

Aber das Interessante ist, dass das Betriebssystem Ihnen sagt: Hey, Ihre App ist in den Hintergrund getreten!

Welches ist genau das, was Sie in erster Linie wissen wollten.

Wie stellen Sie fest, wann Sie zurückgekommen sind?

Nun, das ist ganz einfach, ich bin sicher, Sie haben eine "BaseActivity", also können Sie Ihr onResume () verwenden, um die Tatsache zu kennzeichnen, dass Sie zurück sind. Denn Sie werden nur sagen, dass Sie nicht zurück sind, wenn Sie tatsächlich einen Aufruf der obigen onTrimMemory -Methode erhalten.

Es klappt. Sie erhalten keine False Positives. Wenn eine Aktivität wieder aufgenommen wird, sind Sie zu 100% zurück. Wenn der Benutzer erneut in den Hintergrund wechselt, wird erneut onTrimMemory() aufgerufen.

Sie müssen Ihre Aktivitäten (oder noch besser eine benutzerdefinierte Klasse) abonnieren.

Der einfachste Weg, um sicherzustellen, dass Sie dies immer erhalten, besteht darin, eine einfache Klasse wie die folgende zu erstellen:

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.
    }
}

Um dies zu verwenden, müssen Sie in Ihrer Anwendungsimplementierung ( ein RIGHT? ) folgendermaßen vorgehen:

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

Wenn Sie ein Interface erstellen, können Sie diesem else ein if hinzufügen und ComponentCallbacks (ohne die 2) implementieren, die in allen unter API 14 verwendeten Elementen verwendet wird. Dieser Rückruf hat nur die onLowMemory() method und werden nicht aufgerufen, wenn Sie in den Hintergrund gehen , aber Sie sollten es verwenden, um den Speicher zu verkleinern.

Starten Sie jetzt Ihre App und drücken Sie auf home. Ihre Methode onTrimMemory(final int level) sollte aufgerufen werden (Hinweis: Protokollierung hinzufügen).

Der letzte Schritt besteht darin, die Registrierung für den Rückruf aufzuheben. Der wahrscheinlich beste Ort ist die onTerminate()-Methode Ihrer App , aber , diese Methode wird auf einem realen Gerät nicht aufgerufen:

/**
 * 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.
 */

Wenn Sie also nicht wirklich eine Situation haben, in der Sie nicht mehr registriert werden möchten, können Sie sie ignorieren, da Ihr Prozess auf Betriebssystemebene ohnehin zum Erliegen kommt.

Wenn Sie sich irgendwann dazu entschließen, die Registrierung aufzuheben (wenn Sie beispielsweise einen Abschaltmechanismus für die Bereinigung und den Verlust Ihrer App bereitstellen), können Sie Folgendes tun:

unregisterComponentCallbacks(mMemoryBoss);

Und das ist es.

180

So habe ich es geschafft, das zu lösen. Es geht davon aus, dass die Verwendung einer Zeitreferenz zwischen Aktivitätsübergängen höchstwahrscheinlich einen ausreichenden Beweis dafür liefert, dass eine App "im Hintergrund" ist oder nicht.

Zuerst habe ich eine Android.app.Application-Instanz (nennen wir sie MyApplication) verwendet, die einen Timer, eine TimerTask, eine Konstante, die die maximale Anzahl von Millisekunden darstellt, die der Übergang von einer Aktivität zu einer anderen vernünftigerweise dauern kann (ich bin gegangen) mit einem Wert von 2s) und einem Booleschen Wert, der angibt, ob die App "im Hintergrund" war oder nicht:

public class MyApplication extends Application {

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

Die Anwendung bietet auch zwei Methoden zum Starten und Stoppen des Timers/Tasks:

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;
}

Das letzte Element dieser Lösung ist das Hinzufügen eines Aufrufs zu jeder dieser Methoden aus den Ereignissen onResume () und onPause () aller Aktivitäten oder vorzugsweise in einer Basisaktivität, von der alle Ihre konkreten Aktivitäten erben:

@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();
}

In dem Fall, in dem der Benutzer einfach zwischen den Aktivitäten Ihrer App navigiert, startet die Funktion onPause () der ausgehenden Aktivität den Timer, aber die neu eingegebene Aktivität bricht den Timer fast sofort ab, bevor die maximale Übergangszeit erreicht ist. Und so wäre wasInBackgroundfalse.

Wenn dagegen eine Aktivität vom Startprogramm aus in den Vordergrund kommt, das Gerät aufwacht, das Telefonat beendet usw., wurde höchstwahrscheinlich die Timer-Task ausgeführt, die vor diesem Ereignis ausgeführt wurde, und daher wurde wasInBackground festgelegt bis wahr.

174
d60402

Bearbeiten: Die neuen Architekturkomponenten haben etwas Vielversprechendes gebracht: ProcessLifecycleOwner , siehe @ vokilams Antwort


Die eigentliche Lösung laut einem Google I/O Talk :

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

}

Ja. Ich weiß, es ist schwer zu glauben, dass diese einfache Lösung funktioniert, da wir hier so viele seltsame Lösungen haben.

Aber es gibt Hoffnung.

133

ProcessLifecycleOwner scheint auch eine vielversprechende Lösung zu sein.

ProcessLifecycleOwner sendet ON_START, ON_RESUME Ereignisse, wenn eine erste Aktivität diese Ereignisse durchläuft. ON_PAUSE, ON_STOP, Ereignisse werden mit einer Verzögerung ausgelöst, nachdem eine letzte Aktivität sie durchlaufen hat. Diese Verzögerung ist lang genug, um sicherzustellen, dass ProcessLifecycleOwner keine Ereignisse sendet, wenn Aktivitäten aufgrund einer Konfigurationsänderung zerstört und neu erstellt werden.

Eine Implementierung kann so einfach sein wie

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());

Laut Quellcode ist der aktuelle Verzögerungswert 700ms.

Um diese Funktion nutzen zu können, benötigen Sie außerdem dependencies:

implementation "Android.Arch.lifecycle:extensions:1.1.1" 
annotationProcessor "Android.Arch.lifecycle:compiler:1.1.1"
93
vokilam

Basierend auf der Antwort von Martín Marconcinis (danke!) Habe ich endlich eine zuverlässige (und sehr einfache) Lösung gefunden.

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;
        }
    }
}

Fügen Sie dies dann zu Ihrem onCreate () Ihrer Anwendungsklasse hinzu

public class MyApp extends Android.app.Application {

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

        ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
        registerActivityLifecycleCallbacks(handler);
        registerComponentCallbacks(handler);

    }

}
65
rickul

Wir verwenden diese Methode. Es sieht zu einfach aus, um zu arbeiten, aber es wurde in unserer App ausführlich getestet und funktioniert in allen Fällen überraschend gut. Versuche es.

Im Vordergrund startet Android immer eine neue Aktivität, bevor die vorherige beendet wird. Das ist nicht garantiert, aber so funktioniert es. Übrigens scheint Flurry die gleiche Logik zu verwenden (nur eine Vermutung, ich habe das nicht überprüft, aber es hängt an den gleichen Ereignissen).

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

}

Bearbeiten: Wie aus Kommentaren hervorgeht, sind wir in späteren Versionen des Codes auch zu onStart () übergegangen. Außerdem füge ich Superaufrufe hinzu, die in meinem ursprünglichen Beitrag fehlten, da dies eher ein Konzept als ein Arbeitscode.

61
Nick Frolov

Wenn Ihre App aus mehreren Aktivitäten und/oder gestapelten Aktivitäten besteht, z. B. einem Widget für die Registerkartenleiste, können Sie onPause () und onResume () nicht überschreiben. Dh beim Starten einer neuen Aktivität werden die aktuellen Aktivitäten angehalten, bevor die neue erstellt wird. Gleiches gilt, wenn Sie eine Aktivität beenden (mit der Schaltfläche "Zurück").

Ich habe zwei Methoden gefunden, die wie gewünscht zu funktionieren scheinen.

Die erste Methode erfordert die Berechtigung GET_TASKS und besteht aus einer einfachen Methode, die durch Vergleichen der Paketnamen überprüft, ob die am häufigsten ausgeführte Aktivität auf dem Gerät zur Anwendung gehört:

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;
}

Diese Methode wurde im Droid-Fu-Framework (jetzt Ignition genannt) gefunden.

Die zweite Methode, die ich selbst implementiert habe, erfordert nicht die Berechtigung GET_TASKS, was gut ist. Stattdessen ist die Implementierung etwas komplizierter.

In Ihrer MainApplication-Klasse haben Sie eine Variable, die die Anzahl der ausgeführten Aktivitäten in Ihrer Anwendung protokolliert. In onResume () erhöhen Sie für jede Aktivität die Variable und in onPause () verringern Sie sie.

Wenn die Anzahl der ausgeführten Aktivitäten 0 erreicht, wird die Anwendung in den Hintergrund versetzt, wenn die folgenden Bedingungen erfüllt sind:

  • Die angehaltene Aktivität wird nicht beendet (Schaltfläche "Zurück" wurde verwendet). Dies kann mit der Methode activity.isFinishing () erfolgen.
  • Eine neue Aktivität (gleicher Paketname) wird nicht gestartet. Sie können die Methode startActivity () überschreiben, um eine Variable festzulegen, die dies anzeigt, und sie dann in onPostResume () zurücksetzen. Dies ist die letzte Methode, die ausgeführt wird, wenn eine Aktivität erstellt/fortgesetzt wird.

Wenn Sie feststellen können, dass sich die Anwendung in den Hintergrund zurückgezogen hat, ist es einfach zu erkennen, wenn sie ebenfalls wieder in den Vordergrund gebracht wird.

54
Emil

Erstellen Sie eine Klasse, die Application erweitert. Dann können wir die Überschreibungsmethode onTrimMemory() verwenden.

Um festzustellen, ob die Anwendung im Hintergrund ausgeführt wurde, verwenden wir Folgendes:

 @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
        }
    }
33
Harpreet

Erwägen Sie die Verwendung von onUserLeaveHint. Dies wird nur aufgerufen, wenn Ihre App in den Hintergrund tritt. onPause muss Eckfälle behandeln, da es aus anderen Gründen aufgerufen werden kann. Wenn der Benutzer beispielsweise eine andere Aktivität in Ihrer App öffnet, z. B. Ihre Einstellungsseite, wird die onPause-Methode Ihrer Hauptaktivität aufgerufen, obwohl sie sich noch in Ihrer App befindet. Wenn Sie stattdessen einfach den onUserLeaveHint-Rückruf verwenden können, der genau das tut, was Sie verlangen, führt das Verfolgen des Eingangs zu Fehlern.

Wenn on UserLeaveHint aufgerufen wird, können Sie ein boolesches inBackground-Flag auf true setzen. Wenn onResume aufgerufen wird, wird nur angenommen, dass Sie wieder in den Vordergrund getreten sind, wenn das inBackground-Flag gesetzt ist. Dies liegt daran, dass onResume auch für Ihre Hauptaktivität aufgerufen wird, wenn der Benutzer nur in Ihrem Einstellungsmenü war und die App nie verlassen hat.

Denken Sie daran, dass onUserLeaveHint in Ihrer Einstellungsaktivität aufgerufen wird, wenn der Benutzer auf die Home-Schaltfläche klickt, und wenn er zu onResume zurückkehrt, in Ihrer Einstellungsaktivität. Wenn Sie nur diesen Erkennungscode in Ihrer Hauptaktivität haben, werden Sie diesen Anwendungsfall verpassen. Um diesen Code in all Ihren Aktivitäten zu haben, ohne Code zu duplizieren, müssen Sie eine abstrakte Aktivitätsklasse verwenden, die Activity erweitert, und Ihren gemeinsamen Code einfügen. Dann kann jede Aktivität, die Sie haben, diese abstrakte Aktivität erweitern.

Zum Beispiel:

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 {
    ...
}
18
OldSchool4664

ActivityLifecycleCallbacks könnte von Interesse sein, ist aber nicht gut dokumentiert.

Wenn Sie jedoch registerActivityLifecycleCallbacks () aufrufen, sollten Sie in der Lage sein, Rückrufe zu erhalten, wenn Aktivitäten erstellt, zerstört usw. Sie können getComponentName () für die Aktivität aufrufen.

13
Reno

Das Android.Arch.lifecycle -Paket bietet Klassen und Schnittstellen, mit denen Sie lebenszyklusbezogene Komponenten erstellen können

Ihre Anwendung sollte die LifecycleObserver-Schnittstelle implementieren:

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

Dazu müssen Sie Ihrer build.gradle-Datei diese Abhängigkeit hinzufügen:

dependencies {
    implementation "Android.Arch.lifecycle:extensions:1.1.1"
}

Wie von Google empfohlen, sollten Sie den Code minimieren, der in den Lebenszyklusmethoden von Aktivitäten ausgeführt wird:

Ein übliches Muster besteht darin, die Aktionen der abhängigen Komponenten in den Lebenszyklusmethoden von Aktivitäten und Fragmenten zu implementieren. Dieses Muster führt jedoch zu einer schlechten Organisation des Codes und zur Verbreitung von Fehlern. Durch die Verwendung von Lebenszyklus-fähigen Komponenten können Sie den Code abhängiger Komponenten aus den Lebenszyklusmethoden in die Komponenten selbst verschieben.

Weitere Informationen finden Sie hier: https://developer.Android.com/topic/libraries/architecture/lifecycle

10
matdev

Fügen Sie in Ihrer Anwendung den Rückruf hinzu und überprüfen Sie die Root-Aktivität auf folgende Weise:

@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();
            }
        }
    });
}
8

Ich habe ein Projekt auf Github erstellt App-Vordergrund-Hintergrund-Hören

Erstellen Sie eine BaseActivity für alle Aktivitäten in Ihrer Anwendung.

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

Verwenden Sie nun diese BaseActivity als Superklasse all Ihrer Aktivitäten, wie MainActivity erweitert BaseActivity und onAppStart wird aufgerufen, wenn Sie Ihre Anwendung starten, und onAppPause () wird aufgerufen, wenn die Anwendung von einem beliebigen Bildschirm aus in den Hintergrund wechselt.

6
kiran boghra

Mit ProcessLifecycleOwner ist das ganz einfach

Füge diese Abhängigkeiten hinzu

implementation "Android.Arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "Android.Arch.lifecycle:compiler:$project.archLifecycleVersion"

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

Dann in Ihrer Basisaktivität:

override fun onCreate() {
        super.onCreate()

        ProcessLifecycleOwner.get()
                .lifecycle
                .addObserver(
                        ForegroundBackgroundListener()
                                .also { appObserver = it })
    }

Siehe meinen Artikel zu diesem Thema: https://medium.com/@egek92/wie-ist-die-grundlage-verändert-in-Ihrer-Android-Anwendung-ohne-Wunsch-zu- erkennen -9719cc822c48

6

Es gibt keine einfachen Lebenszyklusmethoden, mit denen Sie feststellen können, wann die gesamte Anwendung im Hintergrund bzw. im Vordergrund ausgeführt wird.

Ich habe das auf einfache Weise getan. Befolgen Sie die nachstehenden Anweisungen, um die Hintergrund-/Vordergrundphase der Anwendung zu erkennen.

Mit ein wenig Umgehung ist es möglich. Hier kommt ActivityLifecycleCallbacks zur Rettung. Lass mich Schritt für Schritt durchgehen.

  1. Erstellen Sie zunächst eine Klasse, die die Schnittstelle Android.app.Application erweitert und die Schnittstelle ActivityLifecycleCallbacks implementiert. Registrieren Sie in der Application.onCreate () den Rückruf.

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
    
  2. Registrieren Sie die Klasse "App" im Manifest wie folgt: <application Android:name=".App".

  3. Wenn sich die App im Vordergrund befindet, befindet sich mindestens eine Aktivität im Status "Gestartet". Wenn sich die App im Hintergrund befindet, befindet sich keine Aktivität im Status "Gestartet".

    Deklarieren Sie 2 Variablen wie unten in der Klasse "App".

    private int activityReferences = 0;
    private boolean isActivityChangingConfigurations = false;
    

    activityReferences behält die Anzahl der Aktivitäten im Zustand started bei. isActivityChangingConfigurations ist ein Flag, das angibt, ob die aktuelle Aktivität wie ein Orientierungsschalter eine Konfigurationsänderung durchläuft.

  4. Mit dem folgenden Code können Sie feststellen, ob die App im Vordergrund steht.

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
    
  5. So erkennen Sie, ob die App im Hintergrund ausgeführt wird.

    @Override
    public void onActivityStopped(Activity activity) {
        isActivityChangingConfigurations = activity.isChangingConfigurations();
        if (--activityReferences == 0 && !isActivityChangingConfigurations) {
            // App enters background
        }
    }
    

Wie es funktioniert:

Dies ist ein kleiner Trick, der mit der Reihenfolge der Aufrufe der Lifecycle-Methoden gemacht wird. Lassen Sie mich ein Szenario durchgehen.

Angenommen, der Benutzer startet die App und die Startaktivität A wird gestartet. Die Lifecycle-Aufrufe lauten:

A.onCreate ()

A.onStart () (++ activityReferences == 1) (App betritt den Vordergrund)

A.onResume ()

Jetzt startet Aktivität A Aktivität B.

A.onPause ()

B.onCreate ()

B.onStart () (++ activityReferences == 2)

B.onResume ()

A.onStop () (--activityReferences == 1)

Dann navigiert der Benutzer von Aktivität B zurück,

B.onPause ()

A.onStart () (++ activityReferences == 2)

A.onResume ()

B.onStop () (--activityReferences == 1)

B.onDestroy ()

Dann drückt der Benutzer die Home-Taste,

A.onPause ()

A.onStop () (--activityReferences == 0) (App wechselt in den Hintergrund)

Falls der Benutzer die Home-Taste von Aktivität B anstelle der Zurück-Taste drückt, bleibt diese unverändert, und activityReferences sind 0. Daher können wir erkennen, wie die App den Hintergrund betritt.

Welche Rolle spielt isActivityChangingConfigurations? Nehmen Sie im obigen Szenario an, dass die Aktivität B die Ausrichtung ändert. Die Rückrufsequenz lautet:

B.onPause ()

B.onStop () (--activityReferences == 0) (App betritt Hintergrund ??)

B.onDestroy ()

B.onCreate ()

B.onStart () (++ activityReferences == 1) (App betritt den Vordergrund ??)

B.onResume ()

Aus diesem Grund haben wir eine zusätzliche Prüfung von isActivityChangingConfigurations, um das Szenario zu vermeiden, wenn die Aktivität die Konfigurationsänderungen durchläuft.

3
Komal Nikhare

Ich habe eine gute Methode gefunden, um Anwendungen zu erkennen, unabhängig davon, ob sie im Vordergrund oder im Hintergrund angezeigt werden. Hier ist mein Code . Hoffe das hilft dir.

/**
 * 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();

}

3
Folyd

Sie können verwenden:

geschützte Leere onRestart ()

Um zwischen Neustarts und Neustarts zu unterscheiden.

enter image description here

3
AYBABTU

Edit 2: Was ich unten geschrieben habe, wird eigentlich nicht funktionieren. Google hat eine App abgelehnt, die einen Aufruf von ActivityManager.getRunningTasks () enthält. Aus der Dokumentation geht hervor, dass diese API nur zu Debug- und Entwicklungszwecken dient. Ich werde diesen Beitrag aktualisieren, sobald ich Zeit habe, das GitHub-Projekt unten mit einem neuen Schema zu aktualisieren, das Timer verwendet und fast genauso gut ist.

Edit 1: Ich habe ein Blog-Post geschrieben und ein einfaches GitHub-Repository erstellt, um dies wirklich einfach zu machen.

Die akzeptierte und die bestbewertete Antwort sind nicht wirklich der beste Ansatz. Die Implementierung von isApplicationBroughtToBackground () für die bestbewertete Antwort behandelt nicht die Situation, in der die Hauptaktivität der Anwendung einer Aktivität unterliegt, die in derselben Anwendung definiert ist, jedoch ein anderes Java -Paket enthält. Ich habe einen Weg gefunden, dies zu tun, der in diesem Fall funktionieren wird.

Rufen Sie dies in onPause () auf und erfahren Sie, ob Ihre Anwendung in den Hintergrund tritt, weil eine andere Anwendung gestartet wurde oder der Benutzer die Home-Taste gedrückt hat.

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;
}
3
Sky Kelsey

Richtige Antwort hier

Erstelle eine Klasse mit dem Namen MyApp wie folgt:

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() {

    }
}

Dann, wo immer Sie wollen (bessere erste Aktivität in der App gestartet), fügen Sie den folgenden Code hinzu:

MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);

Getan! Wenn sich die App im Hintergrund befindet, erhalten wir log status : we are out und wenn wir in die App gehen, erhalten wir log status : we are out

2
erfan

Dies ist die geänderte Version der Antwort von @ d60402: https://stackoverflow.com/a/15573121/4747587

Tun Sie alles, was dort erwähnt wird. Aber anstatt einen Base Activity zu haben und diesen als übergeordnetes Element für jede Aktivität zu definieren und die onResume() und onPause zu überschreiben, gehen Sie wie folgt vor:

Fügen Sie in Ihrer Anwendungsklasse die folgende Zeile hinzu:

registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks-Rückruf);

Dieses callback verfügt über alle Methoden für den Aktivitätslebenszyklus und Sie können jetzt onActivityResumed() und onActivityPaused() überschreiben.

Schauen Sie sich dieses Gist an: https://Gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b

1
Henry

Sie können den ProcessLifecycleOwner verwenden, um einen Lifecycle Observer daran anzuhängen.

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

dann rufen Sie auf der onCreate() Ihrer Application-Klasse Folgendes auf:

ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());

auf diese Weise können Sie die Ereignisse von ON_PAUSE und ON_STOP Ihrer Anwendung erfassen, die auftreten, wenn sie im Hintergrund ausgeführt wird.

1

Ich habe dies mit Google Analytics EasyTracker verwendet und es hat funktioniert. Es kann erweitert werden, um das zu tun, wonach Sie suchen, indem Sie eine einfache Ganzzahl verwenden.

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

            }
        });
    }
}
1
Bill Mote

ich weiß, es ist ein wenig spät, aber ich denke, all diese Antworten haben einige Probleme, während ich es wie unten getan habe und das funktioniert perfekt.

erstellen Sie einen Aktivitätslebenszyklus-Rückruf wie folgt:

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

    }
}

und registrieren Sie es einfach in Ihrer Anwendungsklasse wie folgt:

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}
1
Amir Ziarati

Dies scheint eine der kompliziertesten Fragen in Android zu sein, da Android (zum Zeitpunkt des Schreibens) keine iOS-Entsprechungen für applicationDidEnterBackground() oder applicationWillEnterForeground() Callbacks hat. Ich habe ein AppState Library verwendet, das von @ jenzz zusammengestellt wurde.

[AppState] ist eine einfache, reaktive Android Bibliothek, die auf RxJava basiert und Änderungen des App-Status überwacht. Es benachrichtigt die Abonnenten jedes Mal, wenn die App in den Hintergrund tritt und wieder in den Vordergrund tritt.

Es stellte sich heraus, dass dies genau das ist, was ich brauchte, vor allem, weil meine App mehrere Aktivitäten hatte, so dass das einfache Überprüfen von onStart() oder onStop() auf einer Aktivität sie nicht schneiden würde.

Zuerst habe ich diese Abhängigkeiten zu gradle hinzugefügt:

dependencies {
    compile 'com.jenzz.appstate:appstate:3.0.1'
    compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}

Dann war es eine einfache Sache, diese Zeilen an einer geeigneten Stelle in Ihrem Code hinzuzufügen:

//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;
        }
    }
});

Je nachdem, wie Sie das Observable abonnieren, müssen Sie es möglicherweise abbestellen, um Speicherverluste zu vermeiden. Nochmals mehr Infos auf der Github Seite .

1
Deniz

Da ich keinen Ansatz gefunden habe, der auch die Rotation ohne Überprüfung der Zeitstempel handhabt, dachte ich, ich teile auch mit, wie wir es jetzt in unserer App machen. Die einzige Ergänzung zu dieser Antwort https://stackoverflow.com/a/42679191/5119746 ist, dass wir auch die Ausrichtung berücksichtigen.

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

Für die Rückrufe haben wir dann zuerst den Lebenslauf:

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

         // !!! App came from background !!! Insert code

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

Und onActivityStopped:

   override fun onActivityStopped(activity: Activity?) {

       if (mResumed == mPaused && !mOrientationWasChanged) {

       // !!! App moved to background !!! Insert code

        mAppIsInBackground = true
    }

Und dann kommt hier der Zusatz: Auf Orientierungsänderungen prüfen:

   override fun onConfigurationChanged(newConfig: Configuration) {

       if (newConfig.orientation != mCurrentOrientation) {
           mCurrentOrientation = newConfig.orientation
           mOrientationWasChanged = true
       }
       super.onConfigurationChanged(newConfig)
   }

Das ist es. Hoffe das hilft jemandem :)

1
Julian Horst

Meine Lösung wurde von der Antwort von @ d60402 inspiriert und basiert ebenfalls auf einem Zeitfenster, aber ohne 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();
  }
}

dabei ist SingletonApplication eine Erweiterung der Klasse Application:

public class SingletonApplication extends Application {
  public boolean isInBackground = false;
  public long lastForegroundTransition = 0;
}
1
injecteer

Sie können dies leicht mit Hilfe von ActivityLifecycleCallbacks und ComponentCallbacks2 erreichen.

Erstellen Sie eine Klasse AppLifeCycleHandler, die die oben genannten Schnittstellen implementiert.

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();
  }
}

Implementiere in deiner Klasse, die Application erweitert, AppLifeCycleCallback, um die Rückrufe zu erhalten, wenn die App zwischen Vordergrund und Hintergrund wechselt. Sowas wie unten.

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

Hoffe das hilft.

EDIT Alternativ können Sie jetzt die Life Cycle-fähige Architekturkomponente verwenden.

1
Naveen T P

Dies ist meine Lösung https://github.com/doridori/AndroidUtils/blob/master/App/src/main/Java/com/doridori/lib/app/ActivityCounter.Java

Grundsätzlich müssen die Lebenszyklusmethoden für alle Aktivitäten mit einem Zeitgeber gezählt werden, um Fälle zu erfassen, in denen sich derzeit keine Aktivität im Vordergrund befindet, die App jedoch aktiv ist (d. H. Bei Rotation).

0
Dori

Ich habe es geschafft, die App-Navigation im Hintergrund und zurück in den Vordergrund zu überwachen, indem ich eine BaseActivity implementiert habe, die die Verwendung von Aktivitätsrückrufen für onResume, onPause und onStop ausnutzt. Hier sind meine Implementierungen.

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()
    }
}

Nach dem Erstellen von BaseActivity müssen Sie diese Aktivität nur auf alle Aktivitäten in Ihrer App ausweiten.

Bei dieser Art der Implementierung können Sie Folgendes genau erkennen: - onBackground> App wird in den Hintergrund verschoben. - onForeground> App wird in den Vordergrund verschoben. - onLaunch> App wird gerade geöffnet

Ich hoffe, dies wird dir helfen :)

0
Rhusfer

Hier ist meine Lösung. Registrieren Sie einfach diese ActivityLifecycleCallbacks in Ihrer Hauptanwendungsklasse. In den Kommentaren erwähne ich einen Benutzerprofil Activity Edge-Fall. Diese Aktivität ist einfach eine mit transparenten Rändern.

/**
 * 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)
    {

    }
}
0
Stephen

Wir können diese Lösung mit LiveData erweitern:

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

Jetzt können wir diese LiveData abonnieren und die erforderlichen Ereignisse erfassen. Zum Beispiel:

appForegroundStateLiveData.observeForever { state ->
    when(state) {
        AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
        AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
    }
}
0
Alex Kisel

Meine App muss nach der Rückkehr aus dem Hintergrund neu gestartet werden. Zeigen Sie eine Reihe von Aktivitäten, je nach Kundenwunsch. Nach umfangreichen Recherchen zur Verwaltung der Hintergrund-/Vordergrundübergänge (zwischen iOS und Android sehr unterschiedlich behandelt) habe ich diese Frage gekreuzt. Ich habe hier eine sehr nützliche Hilfe gefunden, insbesondere aus der am häufigsten gewählten und der als richtig gekennzeichneten Antwort. Setzen Sie die Root-Aktivität jedoch JEDES MAL, wenn die App in den Vordergrund tritt, einfach wieder in Kraft. Die Lösung, die für mich funktioniert hat und die meiner Meinung nach am besten geeignet ist - basierend auf den Funktionen der Youtube- und Twitter-Apps - bestand darin, die Antworten von @GirishNair und @ d60402 wie folgt zu kombinieren:

@Override
public void onTrimMemory(int level) {
    if (stateOfLifeCycle.equals("Stop")) {
        startActivityTransitionTimer();
    }

    super.onTrimMemory(level);
}

Mein Timer-Limit ist auf 30 Sekunden eingestellt - ich denke darüber nach, dies ein wenig zu erhöhen.

private final long MAX_ACTIVITY_TRANSITION_TIME = 30000;

Wenn die App in den Vordergrund tritt, neu gestartet oder zerstört wird, rufen Sie die Methode zum Abbrechen des Timers auf.

Bei App-Erweiterung:

@Override
public void onActivityCreated(Activity activity, Bundle arg1) {
    stopActivityTransitionTimer();
    stateOfLifeCycle = "Create";
}

@Override
public void onActivityDestroyed(Activity activity) {
    stopActivityTransitionTimer();
    stateOfLifeCycle = "Destroy";
}

Auf der Aktivität (vorzugsweise auf einer Basisaktivität, die von den anderen geerbt wurde):

@Override
protected void onStart() {
    super.onStart();
    if (App.wasInBackground) {
        stopActivityTransitionTimer();
    }
}

In meinem Fall wird, wenn die App nach Ablauf der maximalen Zeit in den Vordergrund tritt, eine neue Aufgabe erstellt, sodass in der App-Erweiterungsklasse der Befehl stopActivityTransitionTimer () für onActivityCreated () oder onActivityDestroyed () aufgerufen wird, sodass die Methode in einer Aktivität nicht mehr aufgerufen werden muss . Ich hoffe es hilft.

0
Pablo

Wie wäre es mit dieser Lösung

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();
        }
    }
}

Alle Aktivitäten müssen BaseActivity erweitern.

Wenn eine Aktivität eine andere (A-> B) aufruft, ist currentAct nicht gleich getLocalClassName (), da das onStart () der zweiten Aktivität (B) vor dem onStop () der ersten (A) ( https : //developer.Android.com/guide/components/activities.html#CoordinatingActivities ).

Wenn der Benutzer die Home-Taste drückt oder zwischen Anwendungen wechselt, wird nur onStop () aufgerufen und dann ist currentAct gleich getLocalClassName ().

0
Ismael

Durch die Verwendung des folgenden Codes kann ich den Vordergrund- oder Hintergrundstatus meiner App abrufen.

Für weitere Informationen klicken Sie bitte auf hier

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");
    }
  }
}
0
Jitendra Singh

Ich habe dafür gesorgt, dass alle In-App-Aktivitäten mit startActivityForResult gestartet werden, und dann überprüft, ob onActivityResult vor onResume aufgerufen wurde. Wenn dies nicht der Fall ist, bedeutet dies, dass wir gerade von einem Ort außerhalb unserer App zurückgekehrt sind.

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;
}
0
arturh

Diese Antworten scheinen nicht richtig zu sein. Diese Methoden werden auch aufgerufen, wenn eine andere Aktivität beginnt und endet. Sie können ein globales Flag beibehalten (ja, Globals sind schlecht :) und dieses Flag bei jedem Start einer neuen Aktivität auf true setzen. Setzen Sie es im onCreate jeder Aktivität auf false. Dann markieren Sie in der onPause dieses Flag. Wenn es falsch ist, tritt Ihre App in den Hintergrund oder wird beendet.

0
Joris Weimar