Überprüfen, ob eine Android-Anwendung im Hintergrund ausgeführt wird


329

Mit Hintergrund meine ich, dass derzeit keine der Aktivitäten der Anwendung für den Benutzer sichtbar sind.



7
Ich bin hier verwirrt. Warum kann Android dafür keine einfache Überschreibung der Anwendungsklasse bereitstellen? Ist es zu schwierig, dies auf Plattformebene zu wissen? @Override protected void onApplicationSentToBackground () {}
Chuck D

2
@ChuckD - das wäre sinnvoll, was das Android SDK manchmal gerne zu vermeiden scheint. : /
Mark


1
iOS hat dies in den Spaten, nicht sicher, warum Google dies so schwer macht. Es ist so ein offensichtliches Bedürfnis.
Jerry Destremps

Antworten:


388

Es gibt nur wenige Möglichkeiten, um festzustellen, ob Ihre Anwendung im Hintergrund ausgeführt wird, aber nur eine davon ist absolut zuverlässig:

  1. Die richtige Lösung (Kredite gehen zu Dan , CommonsWare und NeTeInStEiN )
    verfolgen Sichtbarkeit Ihrer Anwendung selbst verwendet Activity.onPause, Activity.onResumeMethoden. Speichern Sie den Status "Sichtbarkeit" in einer anderen Klasse. Gute Entscheidungen sind Ihre eigene Implementierung von Applicationoder a Service(es gibt auch einige Variationen dieser Lösung, wenn Sie die Aktivitätssichtbarkeit des Dienstes überprüfen möchten).
     
    Beispiel
    Implementieren Sie eine benutzerdefinierte ApplicationKlasse (beachten Sie die isActivityVisible()statische Methode):

    public class MyApplication extends Application {
    
      public static boolean isActivityVisible() {
        return activityVisible;
      }  
    
      public static void activityResumed() {
        activityVisible = true;
      }
    
      public static void activityPaused() {
        activityVisible = false;
      }
    
      private static boolean activityVisible;
    }
    

    Registrieren Sie Ihre Bewerbungsklasse in AndroidManifest.xml:

    <application
        android:name="your.app.package.MyApplication"
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
    

    Fügen Sie onPauseund onResumezu jedem Activityim Projekt hinzu (Sie können einen gemeinsamen Vorfahren für Ihre Aktivitäten erstellen, wenn Sie möchten, aber wenn Ihre Aktivität bereits von MapActivity/ ListActivityusw. erweitert wurde, müssen Sie immer noch Folgendes von Hand schreiben):

    @Override
    protected void onResume() {
      super.onResume();
      MyApplication.activityResumed();
    }
    
    @Override
    protected void onPause() {
      super.onPause();
      MyApplication.activityPaused();
    }
    

     
    Update
    ActivityLifecycleCallbacks wurden in API Level 14 (Android 4.0) hinzugefügt. Sie können sie verwenden, um zu verfolgen, ob eine Aktivität Ihrer Anwendung derzeit für den Benutzer sichtbar ist. Überprüfen Sie die Antwort von Cornstalks unten für die Details.

  2. Die falsche habe
    ich verwendet, um die folgende Lösung vorzuschlagen:

    Sie können die aktuelle Vordergrund- / Hintergrundanwendung erkennen, mit ActivityManager.getRunningAppProcesses()der eine Liste von RunningAppProcessInfoDatensätzen zurückgegeben wird. Um festzustellen, ob sich Ihre Anwendung im Vordergrund befindet, überprüfen Sie, ob die RunningAppProcessInfo.importanceGleichheit mit RunningAppProcessInfo.IMPORTANCE_FOREGROUNDwhile RunningAppProcessInfo.processNameIhrem Anwendungspaketnamen entspricht.

    Auch wenn Sie ActivityManager.getRunningAppProcesses()von Ihrem Anwendungs-UI-Thread aus aufrufen , wird die Bedeutung IMPORTANCE_FOREGROUNDfür Ihre Aufgabe zurückgegeben, unabhängig davon, ob sie tatsächlich im Vordergrund steht oder nicht. Rufen Sie es im Hintergrund-Thread auf (zum Beispiel über AsyncTask) und es werden korrekte Ergebnisse zurückgegeben.

    Obwohl diese Lösung möglicherweise funktioniert (und tatsächlich die meiste Zeit funktioniert), empfehle ich dringend, sie nicht zu verwenden. Und hier ist warum. Wie Dianne Hackborn schrieb :

    Diese APIs sind nicht für Anwendungen gedacht, auf denen der UI-Fluss basiert, sondern um dem Benutzer die ausgeführten Apps oder einen Task-Manager oder dergleichen anzuzeigen.

    Ja, für diese Dinge wird eine Liste gespeichert. Es ist jedoch in einem anderen Prozess deaktiviert, der von Threads verwaltet wird, die separat von Ihrem ausgeführt werden, und nicht etwas, auf das Sie zählen können, wenn Sie (a) rechtzeitig die richtige Entscheidung treffen oder (b) bei Ihrer Rückkehr ein konsistentes Bild haben. Außerdem wird die Entscheidung darüber, zu welcher "nächsten" Aktivität Sie gehen möchten, immer an dem Punkt getroffen, an dem der Wechsel stattfinden soll, und erst an genau diesem Punkt (an dem der Aktivitätsstatus für den Wechsel kurz gesperrt wird) treffen wir weiß eigentlich genau, was das nächste sein wird.

    Und es ist nicht garantiert, dass die Implementierung und das globale Verhalten in Zukunft gleich bleiben.

    Ich wünschte, ich hätte dies gelesen, bevor ich eine Antwort auf die SO gepostet habe, aber hoffentlich ist es nicht zu spät, meinen Fehler zuzugeben.

  3. Eine andere falsche Lösung, die die
    Droid-Fu- Bibliothek in einer der Antworten ActivityManager.getRunningTasksfür ihre isApplicationBroughtToBackgroundMethode verwendet. Siehe Diannes Kommentar oben und verwende diese Methode auch nicht.


4
Um zu wissen, ob Sie die Home-Taste gedrückt haben oder eine andere App den Fokus gewonnen hat: 1) Implementieren Sie die gute Lösung . 2) Auf OnStopAnfrage an isActivityVisible.
Brais Gabin

28
Leider funktioniert Ihre "richtige" Lösung bei mir nicht. Stellen Sie sich vor, Sie durchlaufen Aktivitäten in Ihrer App. Was dann passiert, ist, dass Ihr 'inForeground'-Flag folgendermaßen lautet: True, False (zwischen der onPause der ersten Aktivität und dem onResume der 2. Aktivität), dann wieder True usw. Sie würden dann eine Art Hysterese benötigen.
Radu

14
Diese Lösung funktioniert nicht, wenn Sie nicht alle Aktivitäten direkt steuern können. Zum Beispiel, wenn Sie eine Aktivität von einem Drittanbieter-SDK haben oder sogar eine ACTION_VIEW-Absicht starten.
user123321

66
Android ist so ein verdammtes Wrack. Niemand dachte, dass jemand Daten auf App-Ebene beibehalten möchte? Gib mir eine Pause

8
Die eigentliche Antwort auf diese Frage lautet anscheinend "Sie können sie nicht richtig überprüfen". Die sogenannte "richtige" Lösung ist bestenfalls eine Problemumgehung, ebenso wie die Verwendung von ActivityLifecycleCallbacks. Sie müssen noch in Betracht ziehen, zwischen Aktivitäten zu wechseln, die als "nicht im Vordergrund" registriert werden. Es macht mich wahnsinnig, dass Sie so etwas nicht überprüfen können ...
Serine

263

VERWENDEN SIE DIESE ANTWORT NICHT

Die Antwort von user1269737 ist der richtige (von Google / Android genehmigte) Weg, dies zu tun . Lies ihre Antwort und gib ihnen eine +1.

Ich werde meine ursprüngliche Antwort hier der Nachwelt zuliebe hinterlassen. Dies war das beste, das es 2012 gab, aber jetzt hat Android die richtige Unterstützung dafür.

Ursprüngliche Antwort

Der Schlüssel wird verwendet ActivityLifecycleCallbacks(beachten Sie, dass hierfür Android API Level 14 (Android 4.0) erforderlich ist). Überprüfen Sie einfach, ob die Anzahl der gestoppten Aktivitäten der Anzahl der gestarteten Aktivitäten entspricht. Wenn sie gleich sind, wird Ihre Anwendung im Hintergrund ausgeführt. Wenn mehr Aktivitäten gestartet wurden, ist Ihre Anwendung weiterhin sichtbar. Wenn mehr wieder aufgenommene als angehaltene Aktivitäten vorhanden sind, ist Ihre Anwendung nicht nur sichtbar, sondern steht auch im Vordergrund. Es gibt 3 Hauptzustände, in denen sich Ihre Aktivität befinden kann: sichtbar und im Vordergrund, sichtbar, aber nicht im Vordergrund und nicht sichtbar und nicht im Vordergrund (dh im Hintergrund).

Das wirklich Schöne an dieser Methode ist, dass sie nicht die asynchronen Probleme getRunningTasks()hat, aber Sie müssen auch nicht alle Activityin Ihrer Anwendung ändern , um etwas in onResumed()/ zu setzen / zu deaktivieren onPaused(). Es sind nur ein paar Codezeilen, die in sich geschlossen sind und in Ihrer gesamten Anwendung funktionieren. Außerdem sind keine funky Berechtigungen erforderlich.

MyLifecycleHandler.java:

public class MyLifecycleHandler implements ActivityLifecycleCallbacks {
    // I use four separate variables here. You can, of course, just use two and
    // increment/decrement them instead of using four and incrementing them all.
    private int resumed;
    private int paused;
    private int started;
    private int stopped;

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {
        ++resumed;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        ++paused;
        android.util.Log.w("test", "application is in foreground: " + (resumed > paused));
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    }

    @Override
    public void onActivityStarted(Activity activity) {
        ++started;
    }

    @Override
    public void onActivityStopped(Activity activity) {
        ++stopped;
        android.util.Log.w("test", "application is visible: " + (started > stopped));
    }

    // If you want a static function you can use to check if your application is
    // foreground/background, you can use the following:
    /*
    // Replace the four variables above with these four
    private static int resumed;
    private static int paused;
    private static int started;
    private static int stopped;

    // And these two public static functions
    public static boolean isApplicationVisible() {
        return started > stopped;
    }

    public static boolean isApplicationInForeground() {
        return resumed > paused;
    }
    */
}

MyApplication.java:

// Don't forget to add it to your manifest by doing
// <application android:name="your.package.MyApplication" ...
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        // Simply add the handler, and that's it! No need to add any code
        // to every activity. Everything is contained in MyLifecycleHandler
        // with just a few lines of code. Now *that's* nice.
        registerActivityLifecycleCallbacks(new MyLifecycleHandler());
    }
}

@Mewzer hat einige gute Fragen zu dieser Methode gestellt, auf die ich in dieser Antwort für alle antworten möchte:

onStop()wird in Situationen mit wenig Speicher nicht aufgerufen; ist das hier ein problem

Nein. Die Dokumente onStop()sagen:

Beachten Sie, dass diese Methode in Situationen mit wenig Arbeitsspeicher, in denen das System nicht über genügend Arbeitsspeicher verfügt, um den Prozess Ihrer Aktivität nach dem Aufruf der Methode onPause () auszuführen, möglicherweise nie aufgerufen wird.

Der Schlüssel hier ist "Halten Sie den Prozess Ihrer Aktivität am Laufen ...". Wenn diese Situation mit wenig Arbeitsspeicher jemals erreicht wird, wird Ihr Prozess tatsächlich beendet (nicht nur Ihre Aktivität). Dies bedeutet, dass diese Methode zur Überprüfung der Hintergründe weiterhin gültig ist, da a) Sie das Mitglied ohnehin nicht auf Hintergründe prüfen können, wenn Ihr Prozess beendet wird, und b) wenn Ihr Prozess erneut gestartet wird (weil eine neue Aktivität erstellt wird), das Mitglied Variablen (ob statisch oder nicht) für MyLifecycleHandlerwerden auf zurückgesetzt 0.

Funktioniert dies bei Konfigurationsänderungen?

Standardmäßig nein. Sie müssen explizit configChanges=orientation|screensize( |mit allem, was Sie wollen) in Ihrer Manifestdatei festlegen und die Konfigurationsänderungen behandeln, sonst wird Ihre Aktivität zerstört und neu erstellt. Wenn Sie dies nicht festlegen, werden die Methoden Ihrer Aktivität in der folgenden Reihenfolge aufgerufen : onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume. Wie Sie sehen können, gibt es keine Überlappung (normalerweise überlappen sich zwei Aktivitäten sehr kurz, wenn zwischen den beiden gewechselt wird. So funktioniert diese Hintergrunderkennungsmethode). Um dies zu umgehen, müssen Sie festlegen configChanges, dass Ihre Aktivität nicht zerstört wird. Zum Glück musste ich einstellenconfigChangesBereits in all meinen Projekten, weil es unerwünscht war, dass meine gesamte Aktivität auf dem Bildschirm zerstört wurde. Drehen / Ändern der Größe, daher habe ich dies nie als problematisch empfunden. (Danke an dpimka für das Auffrischen meines Gedächtnisses und das Korrigieren von mir!)

Eine Note:

Wenn ich hier in dieser Antwort "Hintergrund" gesagt habe, meine ich "Ihre App ist nicht mehr sichtbar". Android-Aktivitäten können sichtbar sein, jedoch nicht im Vordergrund (z. B. wenn eine transparente Benachrichtigungsüberlagerung vorhanden ist). Deshalb habe ich diese Antwort aktualisiert, um dies widerzuspiegeln.

Es ist wichtig zu wissen, dass Android beim Wechseln von Aktivitäten, bei denen nichts im Vordergrund steht, einen seltsamen Moment in der Schwebe hat . Wenn Sie beim Wechseln zwischen Aktivitäten (in derselben App) überprüfen, ob sich Ihre Anwendung im Vordergrund befindet, wird Ihnen aus diesem Grund mitgeteilt, dass Sie nicht im Vordergrund stehen (obwohl Ihre App immer noch die aktive App ist und sichtbar ist ).

Sie können überprüfen , ob Ihre App im Vordergrund in Ihrem sind Activity‚s onPause()Methode nach super.onPause() . Denken Sie nur an den seltsamen Schwebezustand, über den ich gerade gesprochen habe.

Sie können überprüfen , ob Ihre App sichtbar sind (dh , wenn es nicht im Hintergrund ist) in Ihrem Activity‚s onStop()Methode nach super.onStop() .


1
Das sieht interessant aus - aber was passiert in Situationen mit wenig Speicher? Es kann nicht garantiert werden, dass onStop () aufgerufen wird. Könnten wir jemals in die Situation geraten, dass ein onStop () nicht aufgerufen wird und der gestoppte Zähler nicht inkrementiert wird - dies bedeutet, dass die Hintergrundprüfung nicht mehr zuverlässig ist? Oder würde das niemals passieren?
Mewzer

1
Ignoriert dies auch Konfigurationsänderungen? Oder wird die Anwendung als Hintergrund betrachtet, wenn eine Aktivität aufgrund einer Konfigurationsänderung (z. B. Änderung der Ausrichtung) neu erstellt wird? Entschuldigung für die Fragen, aber ich denke, Sie sind auf etwas und sind daran interessiert zu wissen, ob es in diesen Randfällen funktioniert.
Mewzer

1
@Mewzer: Ich wollte als Kommentar antworten, aber es wird ein bisschen Tippen erfordern, um diese Antworten zu erhalten. Schauen Sie also in ein paar Minuten noch einmal vorbei und ich werde meine Antwort bearbeiten.
Cornstalks

1
@Mewzer: Du solltest jetzt deine Antworten finden. Lassen Sie mich wissen, wenn Sie weitere Fragen haben!
Cornstalks

2
@Mewzer: Ich habe eine Notiz nur hinzugefügt , dass Sie von Interesse sein könnten Insbesondere prüfen in Hintergrund versetzen . onStop()Nach super.onStop(). Überprüfen Sie nicht, ob Hintergrundinformationen vorhanden sind onPause().
Cornstalks

186

GOOGLE-LÖSUNG - kein Hack wie bei früheren Lösungen. Verwenden Sie ProcessLifecycleOwner

Kotlin:

class ArchLifecycleApp : Application(), LifecycleObserver {

    override fun onCreate() {
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onAppBackgrounded() {
        //App in background
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onAppForegrounded() {
        // App in foreground
    }

}


Java:

public class ArchLifecycleApp extends Application implements LifecycleObserver {

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onAppBackgrounded() {
        //App in background
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppForegrounded() {
        // App in foreground
    }
}

in app.gradle

dependencies {
    ...
    implementation "android.arch.lifecycle:extensions:1.1.0"

    //New Android X dependency is this - 
    implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"

}

allprojects {
    repositories {
        ...
        google()
        jcenter()
        maven { url 'https://maven.google.com' }
    }
}

Weitere Informationen zu Lifecycle-bezogenen Architekturkomponenten finden Sie hier - https://developer.android.com/topic/libraries/architecture/lifecycle


10
Dies sollte definitiv die richtige Antwort sein! Es funktionierte wie ein Zauber: D
JaviOverflow

2
Dies funktioniert perfekt, ich habe auch ein wenig geändert, damit ich außerhalb dieser Klasse leichter auf den Vordergrund- / Hintergrundstatus zugreifen companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }kann : Dann können Sie den Vordergrundstatus mitArchLifecycleApp.isForeground()
Jose Jet

2
Oh Mann, das ist so viel besser als meine alte Antwort. Habe eine +1 von mir. Ich habe meine Antwort aktualisiert, um Personen auf Ihre zu verweisen.
Cornstalks

2
Obwohl dies eine richtige Antwort ist, müssen keine Rückrufe implementiert werden. Sie können ProcessLifecycleOwner einfach abfragen, wann immer Sie möchten. Überprüfen Sie stackoverflow.com/a/52678290/6600000
Keivan Esbati

2
Wie doc sagt The LifecycleOwner for the whole application process. Note that if your application has multiple processes, this provider does not know about other processes. , funktioniert dies nicht für multiple processesApps. Gibt es eine API, die wir elegant erreichen können?
acntwww

23

Wenn Sie die Support-Bibliothek Version 26 starten , können Sie ProcessLifecycleOwner verwenden. Fügen Sie es einfach wie hier beschrieben zu Ihrer Abhängigkeit hinzu , zum Beispiel:

dependencies {
    def lifecycle_version = "1.1.1"

    // ViewModel and LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    // alternatively - Lifecycles only (no ViewModel or LiveData).
    //     Support library depends on this lightweight import
    implementation "android.arch.lifecycle:runtime:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}

Und ProcessLifecycleOwnerfragen Sie dann einfach ab, wann immer Sie den App-Status wünschen. Beispiele:

//Check if app is in background
ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED;

//Check if app is in foreground
ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);

2
Danke Mann, dies ist der beste und einfachste Weg, der in jedem Teil Ihres Codes geeignet ist, insbesondere wenn Sie fcm verwenden.
Mihae Kheel

Wenn die Anwendung vollständig geschlossen ist, was würde die erste Methode zurückgeben?
Evgeniy Mishustin

@EvgeniyMishustin, das vom aktuellen Status der Anwendung abhängt, aber normalerweise wird CREATED und DESTROYED angezeigt, und danach werden keine neuen Ereignisse mehr angezeigt.
Keivan Esbati

Wo ist also eine "IF" -Anweisung, um zu sehen, ob sich die IF-App im Hintergrund befindet?
Ekashking

@ekashking fügt einfach die gesamte Anweisung in die if-Klausel ein. Zum Beispiel: if (ProcessLifecycleOwner.get (). GetLifecycle (). GetCurrentState (). IsAtLeast (Lifecycle.State.STARTED)) => App steht im Vordergrund
Keivan Esbati

20

Seit Android API 16 gibt es eine einfache Möglichkeit zu überprüfen, ob die App im Vordergrund steht. Es ist vielleicht nicht kinderleicht, aber keine Methoden unter Android sind kinderleicht. Diese Methode ist gut genug, um verwendet zu werden, wenn Ihr Dienst ein Update vom Server erhält und entscheiden muss, ob eine Benachrichtigung angezeigt werden soll oder nicht (denn wenn die Benutzeroberfläche im Vordergrund steht, wird der Benutzer das Update ohne Benachrichtigung bemerken).

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;

Sollte sich dieser Code innerhalb der Serviceklasse oder einer anderen Klasse befinden, z. B. der Anwendungsklasse? Vielen Dank.
Woppi

..wenn Sie es als letzte Zeile verwenden möchten, ist dies nur ein Boolescher Wert, gegen den Sie prüfen würden.
AO_

Dies ist die gleiche Methode wie das AWS Android SDK für Push-Benachrichtigungen.
Spakmad

Beachten Sie Folgendes: "Die Definition des Hintergrunds für Zwecke von Dienstbeschränkungen unterscheidet sich von der Definition, die von der Speicherverwaltung verwendet wird. Eine App befindet sich möglicherweise im Hintergrund, was die Speicherverwaltung betrifft, steht jedoch im Vordergrund, was die Fähigkeit zum Starten von Diensten betrifft.) " developer.android.com/about/versions/oreo/background.html (
ARLabs

Danke, das hat funktioniert! Ich konnte diesen Code in einem verwenden , JobServicezu erkennen , dass der Dienst im Hintergrund ausgeführt wird .
Michael Osofsky

17

Idolons Antwort ist fehleranfällig und viel komplizierter, obwohl hier wiederholt wird. Überprüfen Sie, ob die Android-Anwendung im Vordergrund steht oder nicht. und hier Bestimmen der aktuellen Vordergrundanwendung aus einer Hintergrundaufgabe oder einem Hintergrunddienst

Es gibt einen viel einfacheren Ansatz:

Auf einer BaseActivity, die alle Aktivitäten erweitern:

protected static boolean isVisible = false;

 @Override
 public void onResume()
 {
     super.onResume();
     setVisible(true);
 }


 @Override
 public void onPause()
 {
     super.onPause();
     setVisible(false);
 }

Wann immer Sie überprüfen müssen, ob eine Ihrer Anwendungsaktivitäten im Vordergrund steht, überprüfen Sie einfach isVisible() ;

Um diesen Ansatz zu verstehen, überprüfen Sie diese Antwort des Side-by-Side-Aktivitätslebenszyklus: Aktivität Side-by-Side-Lebenszyklus


3
Idolon's answer is error prone- Leider muss ich dir zustimmen. Basierend auf dem Kommentar von Dianne Hackborn in den Google Groups habe ich meine Antwort aktualisiert. Überprüfen Sie es bitte für die Details.
Idolon

2
Dies ist auch keine narrensichere Lösung. Ein Szenario ist , wenn der Benutzer nach unten gezogen , das Benachrichtigungsfeld, dann weder das onPause, onStopnoch das onResumeEreignis aufgerufen wird. Was machen Sie dann, wenn keines dieser Ereignisse ausgelöst wird?!

Dies führte mich zu dieser Frage: stackoverflow.com/questions/33657102/…
Ruchir Baronia

Leider, aber dieser Code funktioniert falsch, wenn eine Aktivität gestartet wird, wenn ein Bildschirm ausgeschaltet ist. In diesem Fall werden onResume und onPause aufgerufen, isVisible = false zu machen.
CoolMind

@CoolMind Können Sie bitte erklären, in welchem ​​Anwendungsfall Sie eine Aktivität im Hintergrund starten würden?
Neteinstein

11

Ich habe die empfohlene Lösung ausprobiert, die Application.ActivityLifecycleCallbacks und viele andere verwendet, aber sie funktionierten nicht wie erwartet. Dank Sarge habe ich eine ziemlich einfache und unkomplizierte Lösung gefunden, die ich unten beschreibe.

Der Schlüssel der Lösung ist die Tatsache, dass wenn wir ActivityA und ActivityB haben und ActivityB von ActivityA aus aufrufen (und nicht aufrufen ActivityA.finish), ActivityB vor ActivityA onStart()aufgerufen wird .onStop()

Das ist auch der Hauptunterschied zwischen onStop()und dem onPause(), den keiner in den Artikeln erwähnt hat, die ich gelesen habe.

Basierend auf dem Lebenszyklusverhalten dieser Aktivität können Sie einfach zählen, wie oft onStart()und onPause()in welchem Programm Sie aufgerufen wurden. Beachten Sie, dass Sie für jedes Activity Programm die zum Zählen verwendete statische Variable überschreiben onStart()und onStop()dekrementieren müssen. Unten finden Sie den Code, der diese Logik implementiert. Beachten Sie, dass ich eine Klasse verwende, die erweitert Applicationwird. Vergessen Sie also nicht, Manifest.xmlinnerhalb des Application-Tags zu deklarieren : android:name=".Utilities"obwohl dies auch mit einer einfachen benutzerdefinierten Klasse implementiert werden kann.

public class Utilities extends Application
{
    private static int stateCounter;

    public void onCreate()
    {
        super.onCreate();
        stateCounter = 0;
    }

    /**
     * @return true if application is on background
     * */
    public static boolean isApplicationOnBackground()
    {
        return stateCounter == 0;
    }

    //to be called on each Activity onStart()
    public static void activityStarted()
    {
        stateCounter++;
    }

    //to be called on each Activity onStop()
    public static void activityStopped()
    {
        stateCounter--;
    }
}

Jetzt auf jeder Aktivität unseres Programms, sollten wir außer Kraft setzen onStart()und onStop()und Erhöhen / Verringern wie unten dargestellt:

@Override
public void onStart()
{
    super.onStart();
    Utilities.activityStarted();
}

@Override
public void onStop()
{
    Utilities.activityStopped();
    if(Utilities.isApplicationOnBackground())
    {
        //you should want to check here if your application is on background
    }
    super.onStop();
}

Mit dieser Logik gibt es zwei mögliche Fälle:

  1. stateCounter = 0 : Die Anzahl der gestoppten Aktivitäten entspricht der Anzahl der gestarteten Aktivitäten. Dies bedeutet, dass die Anwendung im Hintergrund ausgeführt wird.
  2. stateCounter > 0 : Die Anzahl der gestarteten Anwendungen ist größer als die Anzahl der gestoppten Anwendungen. Dies bedeutet, dass die Anwendung im Vordergrund ausgeführt wird.

Hinweis: Dies stateCounter < 0würde bedeuten, dass mehr Aktivitäten gestoppt als gestartet werden, was unmöglich ist. Wenn Sie auf diesen Fall stoßen, bedeutet dies, dass Sie den Zähler nicht wie gewünscht erhöhen / verringern.

Sie sind bereit zu gehen. Sie sollten überprüfen wollen, ob sich Ihre Anwendung im Hintergrund befindet onStop().


Ich würde bewegen if(Utilities.isApplicationOnBackground()) …zu Utilities. Denn sonst reagiert nur eine bestimmte Aktivität auf das Ereignis.
Anzeigename

10

Es gibt keine Möglichkeit, ohne dass Sie es selbst verfolgen, festzustellen, ob eine Ihrer Aktivitäten sichtbar ist oder nicht. Vielleicht sollten Sie eine neue StackOverflow-Frage stellen und erklären, was Sie mit einer Benutzererfahrung erreichen möchten, damit wir Ihnen möglicherweise alternative Implementierungsideen geben können.


2
In Android haben wir eine Einstellung namens "Hintergrunddaten". Diese Einstellung deaktiviert jede Hintergrunddatenverbindung, wenn die Anwendung im Hintergrund ausgeführt wird. Ich möchte das Umschalten "Hintergrunddaten" für meine Anwendung implementieren. Wenn also keine meiner Aktivitäten für den Benutzer sichtbar ist, möchte ich, dass mein Dienst die Datenübertragung beendet, aber sobald eine meiner Aktivitäten wieder aufgenommen wird, möchte ich dies tun Datenübertragung fortsetzen
cppdev

1
@cppdev: Hoffentlich wird die "Datenübertragung" von a durchgeführt Service. Wenn ja, lassen Sie Ihre Aktivitäten den Dienst benachrichtigen, sobald sie angezeigt werden und verschwinden. Wenn das Servicefeststellt, dass keine Aktivitäten sichtbar sind und dies für einige Zeit so bleibt, stoppen Sie die Datenübertragung am nächsten logischen Stopppunkt. Ja, dies erfordert Code für jede Ihrer Aktivitäten, aber im Moment ist dies unvermeidlich, AFAIK.
CommonsWare

1
Wenn Sie vermeiden möchten, den gemeinsamen Code aller Ihrer Aktivitäten zu kopieren und einzufügen, können Sie eine Klasse erstellen, MyActivityClassdie von Activityden Lebenszyklusmethoden erbt und diese implementiert, und alle Ihre Aktivitäten erben lassen MyActivityClass. Dies wird nicht funktionieren für PreferenceActivityoder MapActivityobwohl (siehe diese Frage )
Guillaume Brunerie

@ CommonsWare Ich hatte mit OnPause () OnResume () versucht, dass es aktiv ist oder nicht, aber wenn meine App nicht im Ansichtsbildschirm angezeigt wird, wenn sie im Hintergrund ausgeführt wird, wie überprüfen Sie, ob sie aktiv ist oder nicht
Manoj

@ CommonsWare Ich hatte mit OnPause () OnResume () versucht, dass es aktiv ist oder nicht, aber wenn meine App nicht im Ansichtsbildschirm angezeigt wird, wenn sie im Hintergrund ausgeführt wird, wie überprüfen Sie, ob sie aktiv ist oder nicht
Manoj

5

Mit ComponentCallbacks2 können Sie feststellen, ob sich die App im Hintergrund befindet. Übrigens ist dieser Rückruf nur in API Level 14 (Ice Cream Sandwich) und höher verfügbar .

Sie erhalten einen Aufruf der Methode:

public abstract void onTrimMemory (int level)

Wenn das Level ist, befindet sich ComponentCallbacks2.TRIM_MEMORY_UI_HIDDENdie App im Hintergrund.

Sie können diese Schnittstelle zu einem Gerät activity, serviceusw.

public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 {
   @Override
   public void onConfigurationChanged(final Configuration newConfig) {

   }

   @Override
   public void onLowMemory() {

   }

   @Override
   public void onTrimMemory(final int level) {
     if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
        // app is in background
     }
   }
}

1
Habe deine Antwort ausprobiert, ist aber nicht so zuverlässig. Der onTrimMemory-Rückruf wird weder beim Sperren des Bildschirms noch beim Drücken der Ein- / Aus-Taste zum Sperren des Bildschirms ausgelöst. TRIM_MEMORY_UI_HIDDEN wird nicht immer zurückgegeben, wenn Ihre App sichtbar ist und Sie eine andere App über eine Statusleistenbenachrichtigung öffnen. Die einzig zuverlässige Lösung besteht darin, die ActivityLifecycleCallbacks zu implementieren und an den jeweiligen Anwendungsfall anzupassen.
Velval

4

Aufbauend auf der Antwort von @Cornstalks mit einigen nützlichen Funktionen.

Zusätzliche Funktionen:

  • Das Singleton-Muster wurde eingeführt, sodass Sie dies überall in der Anwendung tun können: AppLifecycleHandler.isApplicationVisible () und AppLifecycleHandler.isApplicationInForeground ()
  • Behandlung von doppelten Ereignissen hinzugefügt (siehe Kommentare // Maßnahmen zur Änderung der Sichtbarkeit ergreifen und // Maßnahmen zur Änderung des Vordergrunds ergreifen)

App.java

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
    }
}

AppLifecycleHandler.java

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
    private int resumed;
    private int started;

    private final String DebugName = "AppLifecycleHandler";

    private boolean isVisible = false;
    private boolean isInForeground = false;

    private static AppLifecycleHandler instance;

    public static AppLifecycleHandler getInstance() {
        if (instance == null) {
            instance = new AppLifecycleHandler();
        }

        return instance;
    }

    private AppLifecycleHandler() {
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {
        ++resumed;
        android.util.Log.w(DebugName, "onActivityResumed -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
        setForeground((resumed > 0));
    }

    @Override
    public void onActivityPaused(Activity activity) {
        --resumed;
        android.util.Log.w(DebugName, "onActivityPaused -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
        setForeground((resumed > 0));
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    }

    @Override
    public void onActivityStarted(Activity activity) {
        ++started;
        android.util.Log.w(DebugName, "onActivityStarted -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
        setVisible((started > 0));
    }

    @Override
    public void onActivityStopped(Activity activity) {
        --started;
        android.util.Log.w(DebugName, "onActivityStopped -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
        setVisible((started > 0));
    }

    private void setVisible(boolean visible) {
        if (isVisible == visible) {
            // no change
            return;
        }

        // visibility changed
        isVisible = visible;
        android.util.Log.w(DebugName, "App Visiblility Changed -> application is visible: " + isVisible);

        // take some action on change of visibility
    }

    private void setForeground(boolean inForeground) {
        if (isInForeground == inForeground) {
            // no change
            return;
        }

        // in foreground changed
        isInForeground = inForeground;
        android.util.Log.w(DebugName, "App In Foreground Changed -> application is in foreground: " + isInForeground);

        // take some action on change of in foreground

    }

    public static boolean isApplicationVisible() {
        return AppLifecycleHandler.getInstance().started > 0;
    }

    public static boolean isApplicationInForeground() {
        return AppLifecycleHandler.getInstance().resumed > 0;
    }
}

3

Die beste Lösung, die ich mir ausgedacht habe, verwendet Timer.

Sie haben einen Timer in onPause () gestartet und denselben Timer in onResume () abgebrochen. Es gibt 1 Instanz des Timers (normalerweise in der Application-Klasse definiert). Der Timer selbst ist so eingestellt, dass nach 2 Sekunden (oder einem beliebigen Intervall, das Sie für angemessen halten) ein Runnable ausgeführt wird. Wenn der Timer ausgelöst wird, setzen Sie ein Flag, das die Anwendung als im Hintergrund markiert.

In der onResume () -Methode können Sie vor dem Abbrechen des Timers das Hintergrundflag abfragen, um Startvorgänge auszuführen (z. B. Downloads starten oder Standortdienste aktivieren).

Mit dieser Lösung können Sie mehrere Aktivitäten auf dem Backstack ausführen und benötigen keine Implementierungsberechtigungen.

Diese Lösung funktioniert gut, wenn Sie auch einen Ereignisbus verwenden, da Ihr Timer einfach ein Ereignis auslösen kann und verschiedene Teile Ihrer App entsprechend reagieren können.


Ich fange an zu denken, dass dies die beste (wenn auch unglückliche) Lösung ist
dhaag23

Ja, das ist die beste Lösung, die ich auch geschafft habe. Ich musste das Bluetooth-Scannen stoppen, wenn die App nicht im Vordergrund stand, konnte aber nicht einfach die Pause verwenden oder anhalten oder zerstören, da ich nicht ständig anhalten und starten wollte, wenn der Benutzer durch die App navigierte.
CaptRespect

3

Wenn Sie die Entwicklereinstellungen "Aktivitäten nicht beibehalten" aktivieren, ist es nicht ausreichend, nur die Anzahl der erstellten Aktivitäten zu überprüfen. Sie müssen auch isSaveInstanceState überprüfen . Meine benutzerdefinierte Methode isApplicationRunning () prüft, ob die Android-App ausgeführt wird:

Hier mein Arbeitscode:

public class AppLifecycleService implements Application.ActivityLifecycleCallbacks {
    private int created;
    private boolean isSaveInstanceState;
    private static AppLifecycleService instance;

    private final static String TAG = AppLifecycleService.class.getName();

    public static AppLifecycleService getInstance() {
        if (instance == null) {
            instance = new AppLifecycleService();
        }
        return instance;
    }

    public static boolean isApplicationRunning() {
        boolean isApplicationRunning = true;
        if (getCountCreatedActvities() == 0 && !isSaveInstanceState()) {
            isApplicationRunning = false;
        }
        return isApplicationRunning;
    }

    public static boolean isSaveInstanceState() {
        return AppLifecycleService.getInstance().isSaveInstanceState;
    }

    public static int getCountCreatedActvities() {
        return AppLifecycleService.getInstance().created;
    }

    private AppLifecycleService() {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        this.isSaveInstanceState = true;
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        ++created;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        --created;
    }

    @Override
    public void onActivityResumed(Activity activity) {   }

    @Override
    public void onActivityPaused(Activity activity) { }


    @Override
    public void onActivityStarted(Activity activity) { }

    @Override
    public void onActivityStopped(Activity activity) { }        

}

3

Die einzig richtige Lösung:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        MyApp.mainActivity = this;
        super.onCreate(savedInstanceState);
        ...
    }

MyApp.java:

public class MyApp extends Application implements LifecycleObserver {

    public static MainActivity mainActivity = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onAppBackgrounded() {
        // app in background
        if (mainActivity != null) {
            ...
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onAppForegrounded() {
        // app in foreground
        if (mainActivity != null) {
            ...
        }
    }

}

Ich sehe nicht ein, wie diese Lösung mir möglicherweise eine Antwort auf eine einfache Frage innerhalb einer IF-Anweisung zu meiner Aktivität (oder meinem Fragment) geben kann, ob sich meine App im Hintergrund oder im Vordergrund befindet. Wo ist die "IF" -Anweisung ???
Ekashking

2

Um auf die Aussagen von CommonsWare und Key zurückzugreifen, können Sie möglicherweise die Application-Klasse erweitern und alle Ihre Aktivitäten dies für ihre onPause / onResume-Methoden aufrufen lassen. Auf diese Weise können Sie feststellen, welche Aktivitäten sichtbar sind, dies könnte jedoch wahrscheinlich besser gehandhabt werden.

Können Sie genau erläutern, was Sie vorhaben? Wenn Sie sagen, dass Sie im Hintergrund laufen, meinen Sie damit, dass Ihre Anwendung immer noch im Speicher ist, obwohl sie derzeit nicht auf dem Bildschirm angezeigt wird? Haben Sie darüber nachgedacht, Services als dauerhaftere Methode zur Verwaltung Ihrer App zu verwenden, wenn diese nicht im Fokus steht?


In Android haben wir eine Einstellung namens "Hintergrunddaten". Diese Einstellung deaktiviert jede Hintergrunddatenverbindung, wenn die Anwendung im Hintergrund ausgeführt wird. Ich möchte das Umschalten "Hintergrunddaten" für meine Anwendung implementieren. Wenn also keine meiner Aktivitäten für den Benutzer sichtbar ist, möchte ich, dass mein Dienst die Datenübertragung beendet, aber sobald eine meiner Aktivitäten wieder aufgenommen wird, möchte ich dies tun Wiederaufnahme der Datenübertragung
cppdev

1
Applicationhat nicht onPause()oder onResume().
CommonsWare

1
@CommonsWare Sie haben Recht, ich bezog mich auf jede einzelne Aktivität, die die Anwendung in ihrer Pause / ihrem Lebenslauf kontaktierte. Dies ist im Grunde die Idee, die Sie gerade in dem Kommentar zu Ihrer Antwort geteilt haben, obwohl Sie Dienste verwendet haben, die ich für einen intelligenteren Schritt halte.
Dan

2

Ich habe ActivityLifecycleCallbacks selbst implementiert. Ich verwende SherlockActivity, aber für normale Aktivitäten könnte die Klasse funktionieren.

Zunächst erstelle ich eine Schnittstelle, die alle Methoden zum Verfolgen des Aktivitätslebenszyklus enthält:

public interface ActivityLifecycleCallbacks{
    public void onActivityStopped(Activity activity);
    public void onActivityStarted(Activity activity);
    public void onActivitySaveInstanceState(Activity activity, Bundle outState);
    public void onActivityResumed(Activity activity);
    public void onActivityPaused(Activity activity);
    public void onActivityDestroyed(Activity activity);
    public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}

Zweitens habe ich diese Schnittstelle in der Klasse meiner Anwendung implementiert:

public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{

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

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());

    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
    }
}

Drittens erstelle ich eine Klasse, die von SherlockActivity ausgeht:

public class MySherlockActivity extends SherlockActivity {

    protected MyApplication nMyApplication;

    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        nMyApplication = (MyApplication) getApplication();
        nMyApplication.onActivityCreated(this, savedInstanceState);
    }

    protected void onResume() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityResumed(this);
        super.onResume();

    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityPaused(this);
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityDestroyed(this);
        super.onDestroy();
    }

    @Override
    protected void onStart() {
        nMyApplication.onActivityStarted(this);
        super.onStart();
    }

    @Override
    protected void onStop() {
        nMyApplication.onActivityStopped(this);
        super.onStop();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        nMyApplication.onActivitySaveInstanceState(this, outState);
        super.onSaveInstanceState(outState);
    }   
}

Viertens habe ich alle Klassen, die von SherlockActivity ausgehen, durch MySherlockActivity ersetzt:

public class MainActivity extends MySherlockActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

}

Jetzt sehen Sie im Logcat die Protokolle, die in der in MyApplication vorgenommenen Schnittstellenimplementierung programmiert wurden.


1

Die Aktivität wird angehalten, wenn ein Dialog darüber angezeigt wird, sodass alle empfohlenen Lösungen Halblösungen sind. Sie müssen auch Hooks für Dialoge erstellen.



1

Offizielle Dokumente:

Das System unterscheidet zwischen Vordergrund- und Hintergrund-Apps. (Die Definition des Hintergrunds für Zwecke von Dienstbeschränkungen unterscheidet sich von der Definition, die von der Speicherverwaltung verwendet wird. Eine App befindet sich möglicherweise im Hintergrund , was die Speicherverwaltung betrifft , steht jedoch im Vordergrund, was die Fähigkeit zum Starten von Diensten betrifft.) Eine App ist gilt als im Vordergrund, wenn eine der folgenden Aussagen zutrifft:

  1. Es hat eine sichtbare Aktivität, unabhängig davon, ob die Aktivität gestartet oder angehalten wurde.
  2. Es hat einen Vordergrunddienst.
  3. Eine andere Vordergrund-App ist mit der App verbunden, indem sie entweder an einen ihrer Dienste gebunden wird oder einen ihrer Inhaltsanbieter nutzt. Zum Beispiel steht die App im Vordergrund, wenn eine andere App an Folgendes bindet:
    • IME
    • Tapetenservice
    • Benachrichtigungs-Listener
    • Sprach- oder Textdienst

Wenn keine dieser Bedingungen erfüllt ist, wird die App als im Hintergrund betrachtet.


0

Eine andere Lösung für diesen alten Beitrag (für diejenigen, denen es helfen könnte):


<application android:name=".BaseApplication" ... >

public class BaseApplication extends Application {

    private class Status {
        public boolean isVisible = true;
        public boolean isFocused = true;
    }

    private Map<Activity, Status> activities;

    @Override
    public void onCreate() {
        activities = new HashMap<Activity, Status>();
        super.onCreate();
    }

    private boolean hasVisibleActivity() {
        for (Status status : activities.values())
            if (status.isVisible)
                return true;
        return false;
    }

    private boolean hasFocusedActivity() {
        for (Status status : activities.values())
            if (status.isFocused)
                return true;
        return false;
    }

    public void onActivityCreate(Activity activity, boolean isStarting) {
        if (isStarting && activities.isEmpty())
            onApplicationStart();
        activities.put(activity, new Status());
    }

    public void onActivityStart(Activity activity) {
        if (!hasVisibleActivity() && !hasFocusedActivity())
            onApplicationForeground();
        activities.get(activity).isVisible = true;
    }

    public void onActivityWindowFocusChanged(Activity activity, boolean hasFocus) {
        activities.get(activity).isFocused = hasFocus;
    }

    public void onActivityStop(Activity activity, boolean isFinishing) {
        activities.get(activity).isVisible = false;
        if (!isFinishing && !hasVisibleActivity() && !hasFocusedActivity())
            onApplicationBackground();
    }

    public void onActivityDestroy(Activity activity, boolean isFinishing) {
        activities.remove(activity);
        if(isFinishing && activities.isEmpty())
            onApplicationStop();
    }

    private void onApplicationStart() {Log.i(null, "Start");}
    private void onApplicationBackground() {Log.i(null, "Background");}
    private void onApplicationForeground() {Log.i(null, "Foreground");}
    private void onApplicationStop() {Log.i(null, "Stop");}

}

public class MyActivity extends BaseActivity {...}

public class BaseActivity extends Activity {

    private BaseApplication application;

    @Override
    protected void onCreate(Bundle state) {
        application = (BaseApplication) getApplication();
        application.onActivityCreate(this, state == null);
        super.onCreate(state);
    }

    @Override
    protected void onStart() {
        application.onActivityStart(this);
        super.onStart();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        application.onActivityWindowFocusChanged(this, hasFocus);
        super.onWindowFocusChanged(hasFocus);
    }

    @Override
    protected void onStop() {
        application.onActivityStop(this, isFinishing());
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        application.onActivityDestroy(this, isFinishing());
        super.onDestroy();
    }

}

0

Siehe den Kommentar in der Funktion onActivityDestroyed.

Funktioniert mit SDK-Zielversion 14>:

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {

    public static int active = 0;

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
        active--;

        // if active var here ever becomes zero, the app is closed or in background
        if(active == 0){
            ...
        }

    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
        active++;
    }
}

0

Sie sollten eine gemeinsame Einstellung verwenden, um die Eigenschaft zu speichern und mithilfe der Servicebindung aus Ihren Aktivitäten darauf zu reagieren. Wenn Sie nur die Bindung verwenden (dh niemals startService verwenden), wird Ihr Dienst nur ausgeführt, wenn Sie an ihn binden (onResume binden und onPause aufheben), sodass er nur im Vordergrund ausgeführt wird und wenn Sie daran arbeiten möchten Hintergrund können Sie den regulären Start-Stopp-Dienst verwenden.


0

Ich denke, diese Frage sollte klarer sein. Wann? Wo? Was ist Ihre spezifische Situation, die Sie kennenlernen möchten, wenn sich Ihre App im Hintergrund befindet?

Ich stelle meine Lösung nur auf meine Weise vor.
Ich erreiche dies, indem ich das Feld "Wichtigkeit" der RunningAppProcessInfoKlasse in der onStopMethode jeder Aktivität in meiner App verwende. Dies kann einfach erreicht werden, indem BaseActivityfür andere Aktivitäten eine Erweiterung bereitgestellt wird , die die onStopMethode implementiert , um den Wert von "Wichtigkeit" zu überprüfen. Hier ist der Code:

public static boolean isAppRunning(Context context) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> appProcesses = activityManager
        .getRunningAppProcesses();
    for (RunningAppProcessInfo appProcess : appProcesses) {
        if (appProcess.processName.equals(context.getPackageName())) {
            if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {
                return true;
            } 
        }
    }
    return false;
}

Dies ist keine empfohlene Lösung, wie in der Antwort von @ Idolon angegeben.
CoolMind

0

Ich empfehle, diese Seite durchzulesen : http://developer.android.com/reference/android/app/Activity.html

Kurz gesagt, Ihre Aktivität ist nach dem onStop()Aufruf nicht mehr sichtbar .


3
Ich habe ungefähr 10 Aktivitäten in meiner Bewerbung. Ich möchte also wissen, ob keiner von ihnen für den Benutzer sichtbar ist. Insgesamt möchte ich wissen, ob meine Anwendung als Ganzes im Hintergrund ausgeführt wird
cppdev

Dann behalten Sie alle 10-ish im Auge. Oder erklären Sie, wie CommonsWare vorgeschlagen hat, was Sie versuchen.
Schlüssel

3
Das ist nicht richtig. Ihre Aktivität ist sichtbar bis onStop; dazwischen onPauseund onStopes ist sichtbar , aber nicht im Vordergrund .
Nickgrim

@nickgrim: was ist nicht richtig? Ich habe angegeben, dass eine Aktivität nach dem onStop()Aufruf nicht mehr sichtbar ist, was mit dem übereinstimmt, was Sie geschrieben haben.
Schlüssel

@Key: Du hast ursprünglich gesagt, bis onPauseaufgerufen wird: Eine kürzlich vorgenommene Bearbeitung hat dich korrigiert.
Nickgrim


0

Meiner Meinung nach führen viele Antworten zu einer großen Menge an Code und bringen viel Komplexität und Nichtlesbarkeit mit sich.

Wenn Leute auf SO fragen, wie sie zwischen a Serviceund a kommunizieren sollen Activity, empfehle ich normalerweise, den LocalBroadcastManager zu verwenden .


Warum?

Nun, indem Sie die Dokumente zitieren:

  • Sie wissen, dass die von Ihnen gesendeten Daten Ihre App nicht verlassen. Sie müssen sich also keine Sorgen machen, dass private Daten verloren gehen.

  • Andere Anwendungen können diese Broadcasts nicht an Ihre App senden, sodass Sie sich keine Sorgen über Sicherheitslücken machen müssen, die sie ausnutzen können.

  • Es ist effizienter als das Senden einer globalen Sendung über das System.

Nicht in den Dokumenten:

  • Es sind keine externen Bibliotheken erforderlich
  • Der Code ist minimal
  • Es ist schnell zu implementieren und zu verstehen
  • Keine benutzerdefinierten selbst implementierten Rückrufe / Ultra-Singleton / Intra-Prozess-Muster ...
  • Keine starken Referenzen auf Activity, Application...

Beschreibung

Sie möchten also überprüfen, ob sich eines der ActivityElemente derzeit im Vordergrund befindet. Das machst du normalerweise in einer Serviceoder deiner ApplicationKlasse.

Dies bedeutet, dass Ihre ActivityObjekte zum Absender eines Signals werden (ich bin an / ich bin aus). Ihr Servicehingegen wird zum Receiver.

Es gibt zwei Momente, in denen IhreActivity erfahren, ob es im Vordergrund oder im Hintergrund läuft (ja, nur zwei ... nicht 6).

Wenn das Activityin den Vordergrund tritt, wird die onResume()Methode ausgelöst (auch nach aufgerufen)onCreate() ).

Wenn das Activityin den Rücken geht,onPause() wird gerufen.

Dies sind die Momente, in denen Sie Activitydas Signal an Ihre senden solltenService um seinen Zustand zu beschreiben.

Denken Sie bei mehreren Activityan die anActivity in den Hintergrund tritt und dann ein anderes in den Vordergrund tritt.

Die Situation wäre also: *

Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON

Das Service/ Applicationhört einfach weiter auf diese Signale und handelt entsprechend.


Code (TLDR)

Sie Servicemüssen ein implementieren BroadcastReceiver, um auf Signale zu warten.

this.localBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // received data if Activity is on / off
    }
}

public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL") 

Registrieren Sie die ReceiverinService::onCreate()

@Override
protected void onCreate() {
    LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}

Heben Sie die Registrierung auf Service::onDestroy()

@Override
protected void onDestroy() {
    // I'm dead, no need to listen to anything anymore.
    LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}

Jetzt Activitymüssen Sie ihren Zustand mitteilen.

Im Activity::onResume()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

Im Activity::onPause()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

Eine sehr, sehr häufige Situation

Entwickler: Ich möchte Daten von meinem senden Serviceund das aktualisieren Activity. Wie überprüfe ich, ob das Activityim Vordergrund steht?

In der Regel muss nicht überprüft werden, ob sich das Activityim Vordergrund befindet oder nicht. Senden Sie einfach die Daten über LocalBroadcastManagervon Ihrem Service. Wenn das eingeschaltet Activityist, reagiert es und handelt.

Für diese sehr häufige Situation wird der Servicezum Absender und der Activityimplementiert den BroadcastReceiver.

Erstellen Sie also eine Receiverin Ihrem Activity. Registrieren Sie es in onResume()und heben Sie die Registrierung auf onPause(). Es ist nicht erforderlich, die anderen Lebenszyklusmethoden zu verwenden .

Definieren Sie das ReceiverVerhalten in onReceive()(ListView aktualisieren, dies tun, das tun, ...).

Auf diese Weise Activityhört der Wille nur zu, wenn er im Vordergrund ist und nichts passiert, wenn er im Hintergrund ist oder zerstört wird.

Im Falle von mehreren Activitywird jeder, der eingeschaltet Activityist, antworten (wenn sie auch das implementieren Receiver).

Wenn sich alle im Hintergrund befinden, reagiert niemand und das Signal geht einfach verloren.

Senden Sie die Daten von der ServiceVia Intent(siehe Code oben), indem Sie die Signal-ID angeben.



0
fun isAppInForeground(): Boolean {
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager ?: return false

    val appProcesses = activityManager.runningAppProcesses ?: return false

    val packageName = packageName
    for (appProcess in appProcesses) {
        if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName) {
            return true
        }
    }

    return false
}

0

Keine der Antworten passte ganz zum speziellen Fall, wenn Sie wissen möchten, ob sich eine bestimmte Aktivität im Vordergrund befindet und wenn Sie ein SDK ohne direkten Zugriff auf die Anwendung sind. Für mich war ich im Hintergrund-Thread, habe gerade eine Push-Benachrichtigung für eine neue Chat-Nachricht erhalten und möchte nur dann eine Systembenachrichtigung anzeigen, wenn der Chat-Bildschirm nicht im Vordergrund steht.

Mit dem ActivityLifecycleCallbacks, wie in anderen Antworten empfohlen, habe ich eine kleine util-Klasse erstellt, die die Logik enthält, ob MyActivityim Vordergrund steht oder nicht.

class MyActivityMonitor(context: Context) : Application.ActivityLifecycleCallbacks {

private var isMyActivityInForeground = false

init {
    (context.applicationContext as Application).registerActivityLifecycleCallbacks(this)
}

fun isMyActivityForeground() = isMyActivityInForeground

override fun onActivityPaused(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = false
    }
}

override fun onActivityResumed(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = true
    }
}

}}


-1

In meinen Aktivitäten onResume und onPause schreibe ich einen isVisible-Booleschen Wert in SharedPrefences.

    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    Editor editor = sharedPrefs.edit();
    editor.putBoolean("visible", false);
    editor.commit();

Und lesen Sie es woanders, wenn nötig über,

    // Show a Toast Notification if App is not visible (ie in background. Not running, etc) 
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
    if(!sharedPrefs.getBoolean("visible", true)){...}

Vielleicht nicht elegant, aber es funktioniert bei mir ...


-1

Es mag zu spät sein, um zu antworten, aber wenn jemand zu Besuch kommt, dann ist hier die Lösung, die ich vorschlage: Der Grund, warum eine App wissen möchte, dass sie sich im Hintergrund befindet oder in den Vordergrund tritt, kann viele sein, einige sind, 1. Anzeigen von Toasts und Benachrichtigungen, wenn sich der Benutzer in BG befindet. 2.Um einige Aufgaben zum ersten Mal auszuführen, wenn der Benutzer von BG kommt, z. B. eine Umfrage, ein Neuzeichnen usw.

Die Lösung von Idolon und anderen kümmert sich um den ersten Teil, nicht jedoch um den zweiten. Wenn Ihre App mehrere Aktivitäten enthält und der Benutzer zwischen ihnen wechselt, ist das sichtbare Flag zu dem Zeitpunkt, an dem Sie sich in der zweiten Aktivität befinden, falsch. Es kann also nicht deterministisch verwendet werden.

Ich habe etwas getan, was von CommonsWare vorgeschlagen wurde: "Wenn der Dienst feststellt, dass keine Aktivitäten sichtbar sind und dies für einige Zeit so bleibt , stoppen Sie die Datenübertragung am nächsten logischen Stopppunkt."

Die fettgedruckte Zeile ist wichtig und kann verwendet werden, um einen zweiten Punkt zu erzielen. Wenn ich also onActivityPaused () erhalte, ändere ich das sichtbare nicht direkt in false, sondern habe einen Timer von 3 Sekunden (das ist das Maximum, mit dem die nächste Aktivität gestartet werden sollte) und wenn es nicht onActivityResumed gibt ( ) in den nächsten 3 Sekunden aufrufen, sichtbar in false ändern. Ähnlich wie bei onActivityResumed (), wenn es einen Timer gibt, brich ich ihn ab. Zusammenfassend wird das Sichtbare zu isAppInBackground.

Der Code kann leider nicht kopiert und eingefügt werden ...


-3

Ich möchte Ihnen empfehlen, dies auf eine andere Weise zu tun.

Ich denke, Sie möchten den Startbildschirm anzeigen, während das Programm gestartet wird. Wenn es bereits im Backend ausgeführt wird, zeigen Sie es nicht an.

Ihre Anwendung kann kontinuierlich die aktuelle Zeit in eine bestimmte Datei schreiben. Überprüfen Sie beim Starten Ihrer Anwendung den letzten Zeitstempel. Wenn current_time-last_time> der Zeitbereich ist, den Sie zum Schreiben der letzten Zeit angegeben haben, bedeutet dies, dass Ihre Anwendung gestoppt wird, entweder vom System oder vom Benutzer selbst beendet.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.