380 Stimmen

Prüfen, ob eine Android-Anwendung im Hintergrund läuft

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

399voto

Idolon Punkte 27701

Es gibt nur wenige Möglichkeiten zu erkennen, ob Ihre Anwendung im Hintergrund läuft, aber nur eine davon ist absolut zuverlässig:

  1. Die richtige Lösung (Credits gehen an Dan , CommonsWare y NeTeInStEiN )
    Verfolgen Sie die Sichtbarkeit Ihrer Bewerbung selbst mit Activity.onPause , Activity.onResume Methoden. Speichern Sie den Status "Sichtbarkeit" in einer anderen Klasse. Eine gute Wahl ist Ihre eigene Implementierung der Application oder eine Service (es gibt auch ein paar Variationen dieser Lösung, wenn Sie die Sichtbarkeit der Aktivitäten des Dienstes überprüfen möchten).
     
    Beispiel
    Benutzerdefiniert implementieren Application Klasse (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" >

    hinzufügen onPause y onResume für jeden Activity im Projekt (Sie können einen gemeinsamen Vorgänger für Ihre Aktivitäten erstellen, wenn Sie das möchten, aber wenn Ihre Aktivität bereits von MapActivity / ListActivity usw. müssen Sie die folgenden Angaben noch von Hand schreiben):

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

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

  2. Der Falsche
    Ich habe immer die folgende Lösung vorgeschlagen:

    Sie können die aktuelle Vordergrund/Hintergrund-Anwendung mit ActivityManager.getRunningAppProcesses() die eine Liste von RunningAppProcessInfo Aufzeichnungen. Um festzustellen, ob Ihre Anwendung im Vordergrund steht, prüfen Sie RunningAppProcessInfo.importance Feld für Gleichheit mit RunningAppProcessInfo.IMPORTANCE_FOREGROUND alors que RunningAppProcessInfo.processName gleich dem Namen Ihres Anwendungspakets ist.

    Auch wenn Sie anrufen ActivityManager.getRunningAppProcesses() von Ihrem Anwendungs-UI-Thread wird es wichtig zurückgeben IMPORTANCE_FOREGROUND für Ihre Aufgabe, unabhängig davon, ob sie tatsächlich im Vordergrund ist oder nicht. Rufen Sie sie im Hintergrund-Thread auf (zum Beispiel über AsyncTask ) und es wird korrekte Ergebnisse liefern.

    Diese Lösung kann zwar funktionieren (und sie funktioniert in der Tat meistens), aber ich empfehle dringend, davon abzusehen, sie zu verwenden. Und hier ist der Grund dafür. Wie Dianne Hackborn schrieb :

    Diese APIs sind nicht dazu da, dass Anwendungen ihren UI-Flow darauf aufbauen, sondern dazu, dem Benutzer die laufenden Anwendungen oder einen Taskmanager oder ähnliches anzuzeigen.

    Ja, es gibt eine Liste, in der diese Dinge festgehalten werden. Sie befindet sich jedoch in einem anderen Prozess, der von Threads verwaltet wird, die getrennt von Ihrem Prozess laufen, und Sie können sich nicht darauf verlassen, dass Sie (a) sie rechtzeitig sehen, um die richtige Entscheidung zu treffen, oder (b) ein konsistentes Bild haben, wenn Sie zurückkehren. Außerdem wird die Entscheidung über die "nächste" Aktivität immer an dem Punkt getroffen, an dem der Wechsel stattfinden soll, und erst an diesem exakten Punkt (an dem der Aktivitätsstatus kurzzeitig gesperrt wird, um den Wechsel vorzunehmen) wissen wir mit Sicherheit, was als Nächstes ansteht.

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

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

  3. Eine weitere falsche Lösung
    Droiden-Fu Die in einer der Antworten erwähnte Bibliothek verwendet ActivityManager.getRunningTasks für seine isApplicationBroughtToBackground Methode. Siehe Diannes Kommentar oben und verwenden Sie auch diese Methode nicht.

335voto

StepanM Punkte 3872

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

Hier können Sie mehr über die lebenszyklusbezogenen Architekturkomponenten lesen. https://developer.Android.com/topic/libraries/architecture/lifecycle

268voto

Cornstalks Punkte 35631

VERWENDEN SIE DIESE ANTWORT NICHT

Die Antwort von user1269737 ist der richtige (von Google/Android genehmigte) Weg, dies zu tun . Lesen Sie ihre Antwort und geben Sie ihnen ein +1.

Ich werde meine ursprüngliche Antwort der Nachwelt zuliebe hier lassen. Dies war die beste verfügbare zurück im Jahr 2012, aber jetzt Android hat richtige Unterstützung für diese.

Ursprüngliche Antwort

Der Schlüssel ist die Verwendung von ActivityLifecycleCallbacks (Beachten Sie, dass dafür Android API Level 14 (Android 4.0) erforderlich ist). Prüfen Sie einfach, ob die Anzahl der gestoppten Aktivitäten gleich der Anzahl der gestarteten Aktivitäten ist. Wenn sie gleich sind, wird Ihre Anwendung im Hintergrund ausgeführt. Wenn es mehr gestartete Aktivitäten gibt, ist Ihre Anwendung noch sichtbar. Wenn es mehr wieder aufgenommene als angehaltene Aktivitäten gibt, ist Ihre Anwendung nicht nur sichtbar, sondern auch im Vordergrund. Es gibt also 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 (d. h. im Hintergrund).

Das wirklich Schöne an dieser Methode ist, dass sie keine asynchronen Probleme hat getRunningTasks() tut, aber Sie müssen auch nicht jede einzelne Activity in Ihrer Anwendung, um etwas zu setzen/zu löschen in onResumed() / onPaused() . Es sind nur ein paar Zeilen Code, die in sich geschlossen sind und in Ihrer gesamten Anwendung funktionieren. Außerdem sind keine komplizierten 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 eingehen möchte:

onStop() in Situationen mit wenig Speicherplatz nicht aufgerufen wird; ist das hier ein Problem?

Nein. Die Dokumentationen für onStop() sagen:

Beachten Sie, dass diese Methode in Situationen, in denen das System nicht über genügend Speicher verfügt, um den Prozess Ihrer Aktivität nach dem Aufruf der onPause()-Methode weiterlaufen zu lassen, möglicherweise nie aufgerufen wird.

Der Schlüssel dazu ist: "Behalten Sie Ihre Aktivität bei laufender Prozess ..." Wenn dieser Zustand erreicht wird, wird Ihr Prozess tatsächlich beendet (nicht nur Ihre Aktivität). Das bedeutet, dass diese Methode der Überprüfung auf Hintergrundaktivität immer noch gültig ist, weil a) Sie sowieso nicht auf Hintergrundaktivität überprüfen können, wenn Ihr Prozess beendet wird, und b) wenn Ihr Prozess wieder startet (weil eine neue Aktivität erstellt wird), die Mitgliedsvariablen (ob statisch oder nicht) für MyLifecycleHandler wird zurückgesetzt auf 0 .

Funktioniert das auch bei Konfigurationsänderungen?

Standardmäßig, nein. Sie müssen explizit festlegen configChanges=orientation|screensize ( | mit allem anderen, was Sie wollen) in Ihre Manifestdatei einfügen und die Konfigurationsänderungen verarbeiten, sonst wird Ihre Aktivität zerstört und neu erstellt. Wenn Sie dies nicht festlegen, werden die Methoden Ihrer Aktivität in dieser Reihenfolge aufgerufen: onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume . Wie Sie sehen, gibt es keine Überschneidungen (normalerweise überschneiden sich zwei Aktivitäten nur sehr kurz, wenn man zwischen ihnen wechselt, und so funktioniert diese Methode zur Erkennung von Hintergrundaktivitäten). Um dies zu umgehen, müssen Sie configChanges damit Ihre Tätigkeit nicht zerstört wird. Zum Glück musste ich die configChanges bereits in allen meinen Projekten, weil es unerwünscht war, dass meine gesamte Aktivität beim Drehen/Ändern der Größe des Bildschirms zerstört wird, daher habe ich dies nie als problematisch empfunden. (Dank an dpimka für die Auffrischung meines Gedächtnisses und die Richtigstellung!)

Eine Anmerkung:

Wenn ich hier in dieser Antwort von "Hintergrund" gesprochen habe, meinte ich "Ihre App ist nicht mehr sichtbar". Android-Aktivitäten können sichtbar sein, ohne im Vordergrund zu sein (z. B. wenn ein transparentes Benachrichtigungs-Overlay vorhanden ist). Deshalb habe ich diese Antwort aktualisiert, um dies zu berücksichtigen.

Es ist wichtig zu wissen, dass es bei Android einen seltsamen Schwebezustand gibt, wenn man die Aktivitäten wechselt. nichts im Vordergrund steht . Wenn Sie daher beim Wechsel zwischen Aktivitäten (in derselben Anwendung) prüfen, ob sich Ihre Anwendung im Vordergrund befindet, wird Ihnen mitgeteilt, dass Sie sich nicht im Vordergrund befinden (obwohl Ihre Anwendung immer noch die aktive Anwendung ist und sichtbar ist).

Sie können überprüfen, ob sich Ihre Anwendung im Vordergrund befindet, indem Sie in Ihrer Activity 's onPause() Methode après super.onPause() . Erinnern Sie sich einfach an den seltsamen Schwebezustand, von dem ich gerade sprach.

Sie können überprüfen, ob Ihre Anwendung sichtbar ist (d. h. ob sie sich nicht im Hintergrund befindet) in Ihrer Activity 's onStop() Methode après super.onStop() .

28voto

Juozas Kontvainis Punkte 9051

Seit Android API 16 gibt es eine einfache Möglichkeit zu prüfen, ob die App im Vordergrund ist. Es mag nicht narrensicher sein, aber keine Methode auf Android ist narrensicher. Diese Methode ist gut genug, um zu verwenden, wenn Ihr Dienst eine Aktualisierung vom Server erhält und entscheiden muss, ob eine Benachrichtigung angezeigt werden soll oder nicht (denn wenn die Benutzeroberfläche im Vordergrund ist, wird der Benutzer die Aktualisierung ohne Benachrichtigung bemerken).

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

18voto

neteinstein Punkte 17321

Die Antwort von Idolon ist fehleranfällig und viel komplizierter, auch wenn sie hier wiederholt wird prüfen, ob die Android-Anwendung im Vordergrund ist oder nicht? und hier Ermittlung der aktuellen Vordergrundanwendung aus einer Hintergrundaufgabe oder einem Dienst

Es gibt einen viel einfacheren Ansatz:

Bei einem BaseActivity, die alle Activities 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 etwas überprüfen müssen Wenn eine Ihrer Anwendungsaktivitäten im Vordergrund ist, prüfen Sie einfach isVisible() ;

Um diesen Ansatz zu verstehen, sehen Sie sich diese Antwort zum Lebenszyklus von Side-by-Side-Aktivitäten an: Lebenszyklus von Aktivitäten nebeneinander

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X