Sprawdzanie, czy aplikacja na Androida działa w tle

329

Przez tło rozumiem, że żadna aktywność aplikacji nie jest obecnie widoczna dla użytkownika?

cppdev
źródło
7
Jestem tutaj zdezorientowany .. dlaczego Android nie może w tym celu zastąpić klasy aplikacji? Czy zbyt trudno jest o tym wiedzieć na poziomie platformy? @Override chronione void onApplicationSentToBackground () {}
Chuck D
2
@ChuckD - miałoby to sens, co wydaje się czasem unikać zestawu SDK systemu Android. : /
Mark
1
iOS ma to w pikach, nie jestem pewien, dlaczego Google czyni to tak trudnym. To taka oczywista potrzeba.
Jerry Destremps,

Odpowiedzi:

388

Istnieje kilka sposobów na wykrycie, czy aplikacja działa w tle, ale tylko jeden z nich jest całkowicie niezawodny:

  1. Właściwe rozwiązanie (kredyty przejść do Dan , CommonsWare i NeTeInStEiN )
    widoczności toru swojego wniosku przez siebie używając Activity.onPause, Activity.onResumemetod. Przechowuj status „widoczności” w innej klasie. Dobrym wyborem jest Twoja własna implementacja Applicationlub Service(istnieje również kilka odmian tego rozwiązania, jeśli chcesz sprawdzić widoczność aktywności z usługi).
     
    Przykład
    Implementacja niestandardowej Applicationklasy (zwróć uwagę na isActivityVisible()metodę statyczną):

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

    Zarejestruj swoją klasę aplikacji w AndroidManifest.xml:

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

    Dodaj onPausei onResumedo każdego Activityw projekcie (możesz utworzyć wspólnego przodka dla swoich działań, jeśli chcesz, ale jeśli twoja aktywność jest już rozszerzona z MapActivity/ ListActivityitp., Nadal musisz ręcznie napisać następujące):

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

     
    Aktualizacja
    ActivityLifecycleCallback została dodana na poziomie API 14 (Android 4.0). Możesz ich użyć do śledzenia, czy aktywność Twojej aplikacji jest obecnie widoczna dla użytkownika. Sprawdź szczegóły w odpowiedzi Cornstalks poniżej.

  2. Zły
    użyłem, aby zasugerować następujące rozwiązanie:

    Możesz wykryć aktualnie używaną aplikację pierwszego planu / tła, za pomocą ActivityManager.getRunningAppProcesses()której zwraca listę RunningAppProcessInforekordów. Aby ustalić, czy aplikacja znajduje się na pierwszym planie, zaznacz RunningAppProcessInfo.importancepole równości RunningAppProcessInfo.IMPORTANCE_FOREGROUNDwhile, gdy RunningAppProcessInfo.processNamenazwa jest równa nazwie pakietu aplikacji.

    Również jeśli zadzwonisz ActivityManager.getRunningAppProcesses()z wątku interfejsu aplikacji, zwróci znaczenie IMPORTANCE_FOREGROUNDdla twojego zadania, bez względu na to, czy faktycznie znajduje się na pierwszym planie, czy nie. Wywołaj go w wątku w tle (na przykład przez AsyncTask), a zwróci prawidłowe wyniki.

    Chociaż to rozwiązanie może działać (i rzeczywiście działa przez większość czasu), zdecydowanie zalecamy powstrzymanie się od korzystania z niego. I oto dlaczego. Jak napisała Dianne Hackborn :

    Te interfejsy API nie są dostępne dla aplikacji, na których można oprzeć przepływ interfejsu użytkownika, ale do wykonywania takich czynności, jak pokazywanie użytkownikowi uruchomionych aplikacji lub menedżera zadań.

    Tak, w pamięci przechowywana jest lista tych rzeczy. Jest to jednak wyłączone w innym procesie, zarządzanym przez wątki działające osobno od twojego, i nie jest to coś, na co możesz (a) patrzeć w czasie, aby podjąć właściwą decyzję lub (b) mieć spójny obraz do czasu powrotu. Ponadto decyzja o tym, do czego „następne” działanie należy przejść, jest zawsze podejmowana w punkcie, w którym ma nastąpić zmiana, i dopiero w tym momencie (kiedy stan aktywności jest na krótko zablokowany w celu wykonania zmiany) naprawdę wiem na pewno, co będzie dalej.

    A wdrożenie i globalne zachowanie tutaj nie jest zagwarantowane, aby pozostało takie samo w przyszłości.

    Chciałbym przeczytać to przed opublikowaniem odpowiedzi na pisemnym zgłoszeniu zastrzeżeń, ale mam nadzieję, że nie jest za późno, by przyznać się do błędu.

  3. Kolejne błędne rozwiązanie biblioteka
    Droid-Fu wspomniana w jednej z odpowiedzi używa ActivityManager.getRunningTaskstej isApplicationBroughtToBackgroundmetody. Zobacz komentarz Dianne powyżej i nie używaj tej metody.

Idolon
źródło
4
Aby wiedzieć, czy nacisnąłeś przycisk Home lub jakaś inna aplikacja zyskała na ostrości: 1) wdrożyć dobre rozwiązanie . 2) W OnStopzapytaniu do isActivityVisible.
Brais Gabin
28
Niestety twoje „prawidłowe” rozwiązanie nie działa dla mnie. Rozważ, że przechodzisz przez działania w aplikacji. To, co się wtedy dzieje, jest takie, że twoja flaga „inForeground” wygląda następująco: Prawda, Fałsz (między 1.pauzą 1. działania i 2.Resume 2. działania), a następnie ponownie Prawda itp. Potrzebowałbyś wtedy jakiejś histerezy.
Radu,
14
To rozwiązanie nie działa, jeśli nie możesz bezpośrednio kontrolować wszystkich działań. Na przykład, jeśli masz aktywność od sdk innej firmy lub nawet uruchamiasz zamiar ACTION_VIEW.
user123321,
66
Android to taki cholerny wrak. Nikt nie pomyślał, że ktoś może chcieć utrwalić dane na poziomie aplikacji? Daj mi spokój
8
Wygląda na to, że prawdziwą odpowiedzią na to pytanie jest „Nie można poprawnie tego sprawdzić”. Tak zwane „prawidłowe” rozwiązanie jest w najlepszym wypadku obejściem, podobnie jak w przypadku ActivityLifecycleCallbacks. Nadal musisz rozważyć przełączanie się między działaniami, które zostałyby zarejestrowane jako „nie na pierwszym planie”. Przychodzi mi do głowy, że nie można sprawdzić tak prostej rzeczy ...
seryna
263

NIE UŻYWAJ TEJ ODPOWIEDZI

Odpowiedź użytkownika 1269737 to właściwy sposób (zatwierdzony przez Google / Android) . Idź przeczytaj ich odpowiedź i daj im +1.

Zostawię tutaj moją pierwotną odpowiedź ze względu na potomstwo. To był najlepszy dostępny w 2012 roku, ale teraz Android ma odpowiednie wsparcie dla tego.

Oryginalna odpowiedź

Klucz używa ActivityLifecycleCallbacks(należy pamiętać, że wymaga to interfejsu API systemu Android w wersji 14 (Android 4.0)). Wystarczy sprawdzić, czy liczba zatrzymanych działań jest równa liczbie rozpoczętych działań. Jeśli są równe, twoja aplikacja jest w tle. Jeśli jest więcej rozpoczętych działań, aplikacja jest nadal widoczna. Jeśli jest więcej działań wznawianych niż wstrzymanych, aplikacja jest nie tylko widoczna, ale także na pierwszym planie. Są zatem 3 główne stany, w których Twoja aktywność może być: widoczna i na pierwszym planie, widoczna, ale nie na pierwszym planie, i niewidoczna i nie na pierwszym planie (tj. W tle).

Naprawdę fajną rzeczą w tej metodzie jest to, że nie ma problemów asynchronicznych getRunningTasks(), ale nie musisz też modyfikować każdegoActivity aplikacji, aby ustawić / rozbroić coś w onResumed()/ onPaused(). To tylko kilka wierszy kodu, który jest samowystarczalny i działa w całej aplikacji. Ponadto nie są wymagane żadne funky uprawnienia.

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 zadał kilka dobrych pytań na temat tej metody, na które chciałbym odpowiedzieć w tej odpowiedzi dla wszystkich:

onStop()nie jest wywoływany w sytuacjach niskiej pamięci; czy to jest problem?

Nie. Dokumenty dotyczące onStop() mówią:

Zauważ, że ta metoda może nigdy nie zostać wywołana, w sytuacjach braku pamięci, gdy system nie ma wystarczającej ilości pamięci, aby utrzymać proces działania po wywołaniu metody onPause ().

Kluczem jest tutaj: „utrzymuj proces aktywności w działaniu ...” Jeśli kiedykolwiek zostanie osiągnięta ta niska pamięć, proces zostanie faktycznie zabity (nie tylko twoja aktywność). Oznacza to, że ta metoda sprawdzania tła jest nadal ważna, ponieważ a) nie możesz sprawdzić tła, jeśli proces zostanie zabity, i b) jeśli proces rozpocznie się ponownie (ponieważ tworzone jest nowe działanie), członek zmienne (statyczne lub nie) dla MyLifecycleHandlerzostaną zresetowane do 0.

Czy to działa w przypadku zmian konfiguracji?

Domyślnie nie. Musisz jawnie ustawić configChanges=orientation|screensize( już we wszystkich moich projektach, ponieważ nie było pożądane, aby cała moja działalność uległa zniszczeniu podczas obracania / zmiany rozmiaru ekranu, więc nigdy nie uważałem tego za problem. (Dzięki dpimka za odświeżenie mojej pamięci na ten temat) i poprawianie mnie!)| w dowolnym innym celu) w pliku manifestu i obsłużyć zmiany konfiguracji, w przeciwnym razie twoja aktywność zostanie zniszczona i ponownie utworzona. Jeśli nie ustawisz metody Twoja aktywność będzie nazywany w następującej kolejności: onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume. Jak widać, nie ma nakładania się (zwykle dwie czynności nakładają się bardzo krótko podczas przełączania między nimi, tak działa ta metoda wykrywania tła). Aby obejść ten problem, musisz ustawić configChangestak, aby twoja aktywność nie została zniszczona. Na szczęście musiałem ustawićconfigChanges

Jedna uwaga:

Kiedy w tej odpowiedzi powiedziałem „tło”, miałem na myśli „Twoja aplikacja nie jest już widoczna”. Działania na Androidzie mogą być widoczne, ale nie na pierwszym planie (na przykład, jeśli istnieje przezroczysta nakładka powiadomienia). Dlatego zaktualizowałem tę odpowiedź, aby to odzwierciedlić.

Ważne jest, aby wiedzieć, że Android ma dziwny moment zawieszenia podczas przełączania działań, w których nic nie jest na pierwszym planie . Z tego powodu, jeśli podczas przełączania między działaniami (w tej samej aplikacji) sprawdzisz, czy aplikacja znajduje się na pierwszym planie, zostaniesz poinformowany, że nie znajdujesz się na pierwszym planie (mimo że aplikacja jest nadal aktywną aplikacją i jest widoczna ).

Można sprawdzić, czy aplikacja jest na pierwszym planie w twojej Activity„s onPause()metody po super.onPause() . Pamiętaj tylko o dziwnym stanie otchłani, o którym właśnie mówiłem.

Można sprawdzić, czy aplikacja jest widoczny (czyli jeśli nie jest w tle) w swojej Activity„s onStop()metody po super.onStop() .

Łodygi kukurydzy
źródło
1
Wygląda to interesująco - ale co dzieje się w sytuacjach niskiej pamięci? Nie ma gwarancji, że zostanie wywołane onStop (). Czy moglibyśmy kiedykolwiek dojść do sytuacji, w której funkcja onStop () nie jest wywoływana, a licznik zatrzymania nie jest zwiększany - oznacza to, że sprawdzanie w tle nie jest już wiarygodne? A może to się nigdy nie wydarzy?
Mewzer,
1
Czy to również zignoruje zmiany konfiguracji? Czy też aplikacja zostanie uznana za działającą w tle, jeśli działanie zostanie ponownie utworzone w wyniku zmiany konfiguracji (np. Zmiana orientacji) ?. Przepraszamy za pytania, ale myślę, że coś Cię interesuje i chcę wiedzieć, czy to działa w takich przypadkach skrajnych.
Mewzer
1
@Mewzer: Zamierzałem odpowiedzieć jako komentarz, ale zajmie mi to trochę pisania, aby uzyskać te odpowiedzi, więc sprawdź ponownie za kilka minut, a ja zmienię odpowiedź.
Cornstalks,
1
@Mewzer: Powinieneś znaleźć swoje odpowiedzi teraz. Daj mi znać, jeśli będą jakieś inne pytania!
Cornstalks,
2
@Mewzer: Właśnie dodałem notatkę, która może Cię zainteresować. W szczególności sprawdź, czy tło jest onStop()później super.onStop(). Nie sprawdzaj tła w tle onPause().
Cornstalks,
186

ROZWIĄZANIE GOOGLE - nie hack, jak poprzednie rozwiązania. Użyj 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
    }

}


Jawa:

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

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

Możesz przeczytać więcej o komponentach architektury związanych z cyklem życia tutaj - https://developer.android.com/topic/libraries/architecture/lifecycle

użytkownik1269737
źródło
10
To zdecydowanie powinna być poprawna odpowiedź! Działa jak urok: D
JaviOverflow
2
Działa to doskonale, zmodyfikowałem też trochę, aby łatwiej uzyskać dostęp do stanu pierwszego planu / tła poza tą klasą: companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }wtedy możesz uzyskać stan pierwszego planu za pomocąArchLifecycleApp.isForeground()
Jose Jet
2
O rany, to jest o wiele lepsze niż moja stara odpowiedź. Masz ode mnie +1. Zaktualizowałem moją odpowiedź, aby skierować ludzi do twojej.
Cornstalks,
2
Chociaż jest to poprawna odpowiedź, nie ma potrzeby implementowania wywołań zwrotnych, możesz po prostu zapytać ProcessLifecycleOwner, kiedy tylko chcesz. Sprawdź stackoverflow.com/a/52678290/6600000
Keivan Esbati
2
Jak mówi doktor The LifecycleOwner for the whole application process. Note that if your application has multiple processes, this provider does not know about other processes. , to nie działa dla multiple processesaplikacji, czy jest jakiś interfejs API, który możemy elegancko osiągnąć?
acntwww 10.04.19
23

Zaczynając od biblioteki pomocy technicznej w wersji 26, możesz użyć ProcessLifecycleOwner , po prostu dodaj ją do zależności, jak opisano tutaj , na przykład:

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
}

A potem po prostu zapytaj w ProcessLifecycleOwnerdowolnym momencie o stan aplikacji, przykłady:

//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);
Keivan Esbati
źródło
2
Dzięki człowieku, jest to najlepszy i najłatwiejszy sposób, który jest odpowiedni w dowolnej części twojego kodu, szczególnie przy korzystaniu z fcm.
Mihae Kheel
jeśli aplikacja zostanie całkowicie zamknięta, co zwróci pierwsza metoda?
Evgeniy Mishustin
@EvgeniyMishustin, który zależy od aktualnego stanu aplikacji, ale zwykle zobaczysz CREATED, a następnie DESTROYED, a następnie nie będziesz otrzymywać żadnych nowych zdarzeń.
Keivan Esbati
Gdzie jest więc dowolna instrukcja „IF”, aby zobaczyć, czy aplikacja IF znajduje się w tle (na pierwszym planie) ???
ekashking
@ekashking po prostu umieść całą instrukcję w klauzuli if. Na przykład: if (ProcessLifecycleOwner.get (). GetLifecycle (). GetCurrentState (). IsAtLeast (Lifecycle.State.STARTED)) => Aplikacja jest na pierwszym planie
Keivan Esbati
20

Od Android API 16 istnieje prosty sposób, aby sprawdzić, czy aplikacja jest na pierwszym planie. Może nie być niezawodny, ale żadne metody na Androida nie są niezawodne. Ta metoda jest wystarczająca do użycia, gdy usługa otrzymuje aktualizację z serwera i musi zdecydować, czy wyświetlać powiadomienie, czy nie (ponieważ jeśli interfejs użytkownika jest na pierwszym planie, użytkownik zauważy aktualizację bez powiadomienia).

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
Juozas Kontvainis
źródło
czy ten kod powinien należeć do klasy usług, czy innej klasy, np. klasy aplikacji? Wielkie dzięki.
Woppi,
... wszędzie tam, gdzie chcesz go użyć, ponieważ ostatnia linijka to po prostu wartość logiczna, którą sprawdziłeś.
AO_
Jest to ta sama metodologia, co AWS Android SDK dla powiadomień wypychanych.
spakmad
Należy pamiętać, że „Definicja tła do celów ograniczeń usług różni się od definicji używanej przez zarządzanie pamięcią; aplikacja może znajdować się w tle, jeśli chodzi o zarządzanie pamięcią, ale na pierwszym planie, jeśli chodzi o jej zdolność do uruchamiania usług.) „ developer.android.com/about/versions/oreo/background.html (
ARLabs
Dziękuję, zadziałało! Byłem w stanie użyć tego kodu w JobServicecelu wykrycia, że ​​usługa działa w tle.
Michael Osofsky
17

Odpowiedź Idolona jest podatna na błędy i znacznie bardziej skomplikowana, chociaż powtarzam tutaj, czy aplikacja na Androida jest na pierwszym planie, czy nie? i tutaj Określanie bieżącej aplikacji pierwszego planu na podstawie zadania lub usługi w tle

Istnieje o wiele prostsze podejście:

Na zasadzie BaseActivity, która obejmuje wszystkie działania:

protected static boolean isVisible = false;

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


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

Ilekroć musisz sprawdzić, czy któreś z działań aplikacji jest na pierwszym planie, po prostu sprawdź isVisible() ;

Aby zrozumieć to podejście, sprawdź tę odpowiedź w cyklu życia działań obok siebie : Cykl życia działań obok siebie

Neteinstein
źródło
3
Idolon's answer is error prone- niestety muszę się z tobą zgodzić. Na podstawie komentarza Dianne Hackborn w Grupach dyskusyjnych Google zaktualizowałem swoją odpowiedź. Sprawdź proszę, żeby poznać szczegóły.
Idolon
2
To również nie jest niezawodne rozwiązanie. Jeden scenariusz, jeśli użytkownik rozebrany panel powiadomień, a następnie ani onPause, onStopani onResumewydarzeniem jest tzw. Co zatem robisz, jeśli żadne z tych zdarzeń nie zostanie uruchomione ?!
Doprowadziło mnie to do pytania: stackoverflow.com/questions/33657102/…
Ruchir Baronia
Niestety, ale ten kod działa nieprawidłowo, gdy aktywność jest uruchamiana, gdy ekran jest wyłączony. W takim przypadku nazywane są onResume i onPause, dzięki czemu isVisible = false.
CoolMind
@CoolMind Czy możesz wyjaśnić, jaki jest przypadek użycia, w którym można rozpocząć działanie w tle?
neteinstein
11

Wypróbowałem zalecane rozwiązanie, które korzysta z Application.ActivityLifecycleCallbacks i wielu innych, ale nie działały one zgodnie z oczekiwaniami. Dzięki Sarge wpadłem na całkiem proste i proste rozwiązanie, które opisuję poniżej.

Kluczem do rozwiązania jest fakt zrozumienia, że ​​jeśli mamy ActivityA i ActivityB i wywołujemy ActivityB z ActivityA (a nie call ActivityA.finish), wówczas ActivityB onStart()zostanie wywołane przed ActivityA onStop().

To także główna różnica między onStop()ionPause() że nikt nie wspomniał w artykułach czytałem.

Zatem w oparciu o zachowanie cyklu życia tego działania, możesz po prostu policzyć, ile razy wykonałeś onStart()i zostałeś onPause()wywołany w twoim programie. Zauważ, że dla każdego Activity programu musisz przesłonić onStart()i onStop(), aby zwiększyć / zmniejszyć zmienną statyczną używaną do zliczania. Poniżej znajduje się kod implementujący tę logikę. Zauważ, że używam klasy, która się rozszerza Application, więc nie zapomnij zadeklarować Manifest.xmlwewnątrz znacznika aplikacji: android:name=".Utilities"chociaż można go zaimplementować również za pomocą prostej klasy niestandardowej.

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

Teraz na każdej działalności naszego programu, należy nadpisać onStart()i onStop()i góra / dół, jak pokazano poniżej:

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

Przy tej logice istnieją 2 możliwe przypadki:

  1. stateCounter = 0 : Liczba zatrzymanych jest równa liczbie rozpoczętych działań, co oznacza, że ​​aplikacja działa w tle.
  2. stateCounter > 0 : Liczba uruchomionych jest większa niż liczba zatrzymanych, co oznacza, że ​​aplikacja działa na pierwszym planie.

Uwaga: stateCounter < 0oznaczałoby to, że jest więcej zatrzymanych działań niż rozpoczętych, co jest niemożliwe. Jeśli napotkasz ten przypadek, oznacza to, że nie zwiększasz / nie zmniejszasz licznika tak, jak powinieneś.

Jesteś gotowy do pracy. Powinieneś sprawdzić, czy aplikacja znajduje się w tle w tle onStop().

Menelaos Kotsollaris
źródło
Przeprowadziłbym się if(Utilities.isApplicationOnBackground()) …do Utilities. Ponieważ w przeciwnym razie tylko określone działanie zareaguje na zdarzenie.
Nazwa wyświetlana
10

Nie ma sposobu, abyś sam to nie śledził, aby ustalić, czy którekolwiek z twoich działań są widoczne, czy nie. Być może powinieneś rozważyć zadanie nowego pytania StackOverflow, wyjaśniając, co próbujesz osiągnąć na podstawie doświadczenia użytkownika, abyśmy mogli zaproponować alternatywne pomysły na implementację.

CommonsWare
źródło
2
W Androidzie mamy ustawienie o nazwie „Dane w tle”. To ustawienie włącza dowolne połączenie danych w tle, gdy aplikacja działa w tle. Chcę zaimplementować przełącznik „Dane w tle” dla mojej aplikacji, więc gdy żadna z moich działań nie jest widoczna dla użytkownika, chciałbym, aby moja usługa przestała przesyłać dane, ale w chwili wznowienia jednej z moich czynności, chciałbym wznowić przesyłanie danych
cppdev
1
@cppdev: Mam nadzieję, że „transfer danych” jest prowadzony przez Service. Jeśli tak, poproś, aby twoje działania powiadomiły serwis w miarę ich pojawiania się i znikania. Jeśli Servicestwierdzi, że nie są widoczne żadne działania i tak pozostanie przez pewien czas, zatrzymaj przesyłanie danych w następnym logicznym punkcie zatrzymania. Tak, będzie to wymagało kodu dla każdej twojej aktywności, ale w tej chwili jest to nieuniknione AFAIK.
CommonsWare
1
Jeśli chcesz uniknąć kopiowania i wklejania wspólnego kodu między wszystkimi Twoimi działaniami, możesz utworzyć klasę MyActivityClassdziedziczącą Activityi wdrażającą metody cyklu życia, a także dziedziczyć wszystkie swoje działania MyActivityClass. To nie zadziała PreferenceActivityani MapActivitynie (patrz to pytanie )
Guillaume Brunerie,
@CommonsWare Próbowałem z OnPause () OnResume (), że jest aktywny, ale jeśli moja aplikacja nie wyświetla widoku na ekranie, jeśli działa w tle, sprawdź, czy jest aktywna, czy nie
Manoj
@CommonsWare Próbowałem z OnPause () OnResume (), że jest aktywny, ale jeśli moja aplikacja nie wyświetla widoku na ekranie, jeśli działa w tle, sprawdź, czy jest aktywna, czy nie
Manoj
5

Za pomocą ComponentCallbacks2 można wykryć, czy aplikacja działa w tle. BTW to oddzwonienie jest dostępne tylko na poziomie API 14 (Ice Cream Sandwich) i wyższym.

Otrzymasz wywołanie do metody:

public abstract void onTrimMemory (int level)

jeśli poziom jest ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN to aplikacja jest w tle.

Można wdrożyć ten interfejs do activity, serviceitp

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
     }
   }
}
Rohit Arya
źródło
1
Próbowałem odpowiedzi, ale nie jest tak wiarygodna. wywołanie zwrotne onTrimMemory nie zostanie uruchomione, gdy ekran jest zablokowany, ani po naciśnięciu przycisku „zasilania”, aby zablokować ekran. Nie zawsze też zwróci TRIM_MEMORY_UI_HIDDEN, jeśli Twoja aplikacja jest widoczna i otworzysz inną aplikację za pomocą powiadomienia na pasku stanu. Jedynym niezawodnym rozwiązaniem jest wdrożenie ActivityLifecycleCallback i dostosowanie go do przypadków użycia.
velval
4

Opierając się na odpowiedzi @Cornstalks, zawiera kilka przydatnych funkcji.

Dodatkowe cechy:

  • wprowadzono wzorzec singletonu, dzięki czemu można to zrobić w dowolnym miejscu aplikacji: AppLifecycleHandler.isApplicationVisible () i AppLifecycleHandler.isApplicationInForeground ()
  • dodano obsługę zduplikowanych zdarzeń (patrz komentarze // podejmij działania w sprawie zmiany widoczności i // podejmij działania w przypadku zmiany na pierwszym planie)

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;
    }
}
seb
źródło
3

Najlepsze rozwiązanie, jakie wymyśliłem, wykorzystuje timery.

Uruchomiłeś timer w onPause () i anulowałeś ten sam timer w onResume (), istnieje 1 instancja Timera (zwykle zdefiniowana w klasie Application). Sam zegar jest ustawiony tak, aby uruchamiał Runnable po 2 sekundach (lub w dowolnym innym okresie, który uważasz za odpowiedni), kiedy zegar odpala, ustaw flagę oznaczającą aplikację jako działającą w tle.

W metodzie onResume () przed anulowaniem timera możesz zapytać flagę tła, aby wykonać dowolne operacje uruchamiania (np. Rozpocząć pobieranie lub włączyć usługi lokalizacyjne).

To rozwiązanie pozwala mieć kilka działań na tylnym stosie i nie wymaga żadnych uprawnień do wdrożenia.

To rozwiązanie działa dobrze, jeśli używasz magistrali zdarzeń, ponieważ zegar może po prostu odpalić zdarzenie, a różne części aplikacji mogą odpowiednio zareagować.

Andrew Kelly
źródło
Zaczynam myśleć, że to najlepsze (choć niefortunne) rozwiązanie
dhaag23,
Tak, to najlepsze rozwiązanie, jakie udało mi się zrealizować. Musiałem zatrzymać skanowanie bluetooth, gdy aplikacja nie była na pierwszym planie, ale nie mogłem po prostu użyć opcji onpause lub stop lub zniszczyć, ponieważ nie chciałem ciągle zatrzymywać się i uruchamiać, gdy użytkownik porusza się po aplikacji.
CaptRespect
3

Jeśli włączysz ustawienia programisty „Nie zachowuj aktywności” - sprawdź tylko liczbę utworzonych działań. Musisz również sprawdzić isSaveInstanceState . Moja metoda niestandardowa toApplicationRunning () to sprawdzanie przez aplikację czy aplikacja na Androida działa:

Oto mój kod pracy:

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

}
subskrybent
źródło
3

Jedyne prawidłowe rozwiązanie:

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

}
Dmitry
źródło
Nie rozumiem, w jaki sposób to rozwiązanie może dać mi odpowiedź na proste pytanie w instrukcji IF dotyczące mojej aktywności (lub fragmentu), czy moja aplikacja jest w tle, czy na pierwszym planie. Gdzie jest wyrażenie „JEŻELI”?
ekashking
2

Aby odnieść się do tego, co powiedzieli CommonsWare i Key, być może mógłbyś rozszerzyć klasę aplikacji i wszystkie twoje działania wywoływałyby to przy użyciu metod onPause / onResume. To pozwoli ci dowiedzieć się, które Działania są widoczne, ale prawdopodobnie można to lepiej rozwiązać.

Czy potrafisz dokładnie wyjaśnić, co masz na myśli? Kiedy mówisz, że działasz w tle, masz na myśli po prostu pozostawanie aplikacji w pamięci, nawet jeśli nie jest ona aktualnie wyświetlana na ekranie? Czy zastanawiałeś się nad użyciem Usług jako bardziej trwałego sposobu zarządzania aplikacją, gdy nie jest ona w centrum uwagi?

Dan
źródło
W Androidzie mamy ustawienie o nazwie „Dane w tle”. To ustawienie włącza dowolne połączenie danych w tle, gdy aplikacja działa w tle. Chcę zaimplementować przełącznik „Dane w tle” dla mojej aplikacji, więc gdy żadna z moich działań nie jest widoczna dla użytkownika, chciałbym, aby moja usługa przestała przesyłać dane, ale gdy tylko jedna z moich czynności zostanie wznowiona, chciałbym wznowić przesyłanie danych
cppdev
1
Applicationnie ma onPause()lub onResume().
CommonsWare
1
@CommonsWare Masz rację, miałem na myśli każdą indywidualną Aktywność kontaktującą się z Aplikacją podczas pauzy / wznowienia. Jest to w zasadzie pomysł, który właśnie podzieliłeś się z komentarzem do swojej odpowiedzi, chociaż korzystałeś z usług, które moim zdaniem są mądrzejsze.
Dan
2

Zrobiłem własną implementację ActivityLifecycleCallbacks. Korzystam z SherlockActivity, ale dla normalnej klasy Activity może działać.

Po pierwsze, tworzę interfejs, który ma wszystkie metody śledzenia cyklu życia działań:

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

Po drugie, zaimplementowałem ten interfejs w klasie mojej aplikacji:

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

Po trzecie, tworzę klasę, która opiera się na SherlockActivity:

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

Po czwarte, wszystkie klasy należące do SherlockActivity zastąpiłem MySherlockActivity:

public class MainActivity extends MySherlockActivity{

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

}

Teraz w logcat zobaczysz dzienniki zaprogramowane w implementacji interfejsu wykonanej w MyApplication.

ClarkXP
źródło
1

Aktywność zostaje wstrzymana, gdy okno dialogowe pojawi się nad nią, więc wszystkie zalecane rozwiązania są pół-rozwiązaniami. Musisz także utworzyć zaczepy dla okien dialogowych.

konmik
źródło
1

Oficjalne dokumenty:

System rozróżnia aplikacje pierwszego planu i tła. (Definicja tła do celów ograniczeń usług różni się od definicji stosowanej przez zarządzanie pamięcią; aplikacja może znajdować się w tle, jeśli chodzi o zarządzanie pamięcią , ale na pierwszym planie, jeśli chodzi o jej zdolność do uruchamiania usług.) Aplikacja jest uważane za znajdujące się na pierwszym planie, jeżeli spełniony jest jeden z poniższych warunków:

  1. Ma widoczną aktywność, niezależnie od tego, czy aktywność została uruchomiona, czy wstrzymana.
  2. Ma usługę pierwszego planu.
  3. Inna aplikacja na pierwszym planie jest połączona z aplikacją, albo przez powiązanie z jedną z jej usług, albo przez wykorzystanie jednego z jej dostawców treści. Na przykład aplikacja jest na pierwszym planie, jeśli inna aplikacja wiąże się z jej:
    • JA JA
    • Usługa tapet
    • Odbiornik powiadomień
    • Usługi głosowe lub tekstowe

Jeśli żaden z tych warunków nie jest spełniony, aplikacja jest uznawana za działającą w tle.

Uddhav Gautam
źródło
0

Inne rozwiązanie dla tego starego postu (dla tych, które mogą pomóc):


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

}
alex
źródło
0

Zobacz komentarz w funkcji onActivityDestroyed.

Działa z docelową wersją zestawu SDK 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++;
    }
}
ramden
źródło
0

Powinieneś użyć wspólnych preferencji, aby przechowywać właściwość i postępować zgodnie z nią, korzystając z powiązania usługi z twoich działań. Jeśli użyjesz tylko powiązania ((nigdy nie używaj startService)), twoja usługa będzie działać tylko wtedy, gdy się z nią powiążesz (powiązaj onResume i unbind onPause), co sprawi, że będzie działać tylko na pierwszym planie, a jeśli chcesz pracować w tle możesz skorzystać ze zwykłej usługi start-stop.

Ofek Ron
źródło
0

Myślę, że to pytanie powinno być bardziej jasne. Kiedy? Gdzie? W jakiej konkretnej sytuacji chcesz się dowiedzieć, jeśli Twoja aplikacja działa w tle?

Po prostu przedstawiam swoje rozwiązanie na swój sposób.
Robię to za pomocą pola „ważność” RunningAppProcessInfoklasy w onStopmetodzie każdej czynności w mojej aplikacji, co można po prostu osiągnąć, udostępniając opcję BaseActivityrozszerzenia innych działań, która implementuje onStopmetodę sprawdzania wartości „ważności”. Oto kod:

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;
}
wxf04125
źródło
To nie jest zalecane rozwiązanie, jak stwierdzono w odpowiedzi @ Idolon.
CoolMind,
0

Polecam przeczytanie tej strony: http://developer.android.com/reference/android/app/Activity.html

Krótko mówiąc, twoja aktywność nie jest już widoczna po onStop()wywołaniu.

Klucz
źródło
3
W mojej aplikacji mam około 10 działań. Chcę więc wiedzieć, czy żadne z nich nie jest widoczne dla użytkownika. W sumie chcę wiedzieć, czy moja aplikacja jako całość działa w tle
cppdev,
Zatem śledzisz wszystkie 10-krotnie. Lub, jak sugeruje CommonsWare, wyjaśnij, co próbujesz zrobić.
Klucz
3
To nie jest poprawne Twoja aktywność jest widoczna do onStop; pomiędzy onPausei onStopjest widoczny , ale nie na pierwszym planie .
nickgrim
@nickgrim: co nie jest poprawne? Stwierdziłem, że aktywność nie jest już widoczna po onStop()wywołaniu, co jest zgodne z tym, co napisałeś.
Klucz
@Klucz: Pierwotnie powiedziałeś, dopóki się onPausenie nazywa: ostatnia edycja cię poprawiła.
nickgrim
0

Co z użyciem getApplicationState (). IsInForeground ()?

Jonathan
źródło
0

Moim zdaniem, wiele odpowiedzi wprowadza duże obciążenie kodu i zapewnia dużą złożoność i brak możliwości odczytu.

Gdy ludzie pytają na SO, jak komunikować się między a, Servicea Activityzwykle radzę skorzystać z LocalBroadcastManager .


Dlaczego?

Cytując dokumenty:

  • Wiesz, że transmitowane dane nie opuszczą Twojej aplikacji, więc nie musisz się martwić o wyciek prywatnych danych.

  • Inne aplikacje nie mogą wysłać tych transmisji do Twojej aplikacji, więc nie musisz się martwić o luki w zabezpieczeniach, które mogą wykorzystać.

  • Jest to bardziej wydajne niż wysyłanie globalnej transmisji przez system.

Nie w dokumentacji:

  • Nie wymaga bibliotek zewnętrznych
  • Kod jest minimalny
  • Jest szybki do wdrożenia i zrozumienia
  • Brak własnych niestandardowych wzorca wywołania zwrotnego / ultra-singletonu / wewnątrzprocesowego ...
  • Brak silnych odnośników na Activity, Application...

Opis

Więc chcesz sprawdzić, czy któryś z nich Activityjest obecnie na pierwszym planie. Zazwyczaj robisz to w klasie Servicelub w Applicationklasie.

Oznacza to, że twoje Activityobiekty stają się nadawcą sygnału (jestem włączony / jestem wyłączony). Twój Servicenatomiast staje się Receiver.

dwa momenty, w których twójActivity mówi ci, czy idzie na pierwszym planie, czy w tle (tak, tylko dwa ... nie 6).

Po przejściu Activityna pierwszy plan onResume()uruchamiana jest metoda (wywoływana również po onCreate()).

Gdy Activityidzie z tyłu, onPause()nazywa się.

To są momenty, w których Activitypowinieneś wysłać sygnał, Serviceaby opisał swój stan.

W przypadku wielu Activity, pamiętaj oActivity najpierw przechodzi w tło, a potem na pierwszy plan.

Sytuacja wyglądałaby następująco: *

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

Service/ ApplicationPo prostu zachować słuchanie tych sygnałów i podjąć odpowiednie działania.


Kod (TLDR)

Twój Servicemusi implementować BroadcastReceiveraby słuchać sygnałów.

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

Zarejestruj się ReceiverwService::onCreate()

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

Wyrejestruj go w Service::onDestroy()

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

Teraz wasze Activitymuszą zakomunikować swój stan.

W Activity::onResume()

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

W Activity::onPause()

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

Bardzo, bardzo powszechna sytuacja

Deweloper: Chcę wysłać dane z mojego Servicei zaktualizować Activity. Jak sprawdzić, czy Activityjest na pierwszym planie?

Zazwyczaj nie ma potrzeby sprawdzania, czy Activityjest na pierwszym planie, czy nie. Po prostu wyślij dane za pośrednictwem LocalBroadcastManagerswojego Service. Jeśli Activityjest włączony, zareaguje i zadziała.

W tej bardzo powszechnej sytuacji Servicestaje się nadawcą, a Activityimplementuje BroadcastReceiver.

Więc stwórz Receiverw swoim Activity. Zarejestruj się onResume()i wyrejestruj onPause(). Nie ma potrzeby stosowania innych metod cyklu życia .

Zdefiniuj Receiverzachowanie w onReceive()(zaktualizuj ListView, zrób to, zrób to ...).

W ten sposób Activitybędą nasłuchiwać tylko wtedy, gdy będzie na pierwszym planie i nic się nie stanie, jeśli będzie z tyłu lub zostanie zniszczone.

W przypadku wielokrotności Activity, cokolwiekActivity jest włączone, zareaguje (jeśli implementują również Receiver).

Jeśli wszyscy są w tle, nikt nie zareaguje, a sygnał po prostu zginie.

Wyślij dane z Servicevia Intent(patrz kod powyżej), podając identyfikator sygnału.


  • Z wyjątkiem obsługi wielu okien . Może to być trudne (proszę przetestować w razie potrzeby) ...
Marko Pacak
źródło
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
}
ayoub laaziz
źródło
0

Żadna z odpowiedzi nie pasowała do konkretnego przypadku, jeśli chcesz wiedzieć, czy konkretne działanie znajduje się w terenie i czy jesteś SDK bez bezpośredniego dostępu do aplikacji. Dla mnie byłem w tle, ponieważ właśnie otrzymałem powiadomienie push dla nowej wiadomości czatu i chcę wyświetlać powiadomienie systemowe tylko wtedy, gdy ekran czatu nie jest na pierwszym planie.

Korzystając z ActivityLifecycleCallbackstego, jak zalecono w innych odpowiedziach, stworzyłem małą klasę użytkową, która zawiera logikę, czy MyActivityjest na pierwszym planie, czy nie.

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

}

szkocja
źródło
-1

W moich działaniach onResume i onPause piszę wartość logiczną isVisible do SharedPrefences.

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

W razie potrzeby przeczytaj go gdzie indziej,

    // 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)){...}

Może nie elegancki, ale działa dla mnie ...

mtwagner
źródło
-1

Może być za późno na odpowiedź, ale jeśli ktoś przyjeżdża, oto rozwiązanie, które sugeruję: Powodem, dla którego aplikacja chce wiedzieć, że jest w tle lub na pierwszym planie może być wiele, kilka to, 1. Aby wyświetlić tosty i powiadomienia, gdy użytkownik jest w BG. 2. Aby wykonać niektóre zadania po raz pierwszy użytkownik pochodzi z BG, takie jak ankieta, przerysowanie itp.

Rozwiązanie Idolon i inni zajmuje się pierwszą częścią, ale nie drugą. Jeśli w Twojej aplikacji jest wiele aktywności, a użytkownik przełącza się między nimi, to do czasu drugiej aktywności widoczna flaga będzie fałszywa. Dlatego nie można go stosować deterministycznie.

Zrobiłem coś, co zostało zasugerowane przez CommonsWare: „Jeśli usługa stwierdzi, że nie widać żadnych działań, i tak pozostanie przez pewien czas , zatrzymaj przesyłanie danych w następnym logicznym punkcie zatrzymania”.

Pogrubiona linia jest ważna i można jej użyć do osiągnięcia drugiego przedmiotu. Więc po zrobieniu onActivityPaused (), nie zmieniaj bezpośrednio parametru vis na false, zamiast tego mam licznik 3 sekund (to jest maksimum, że następne działanie powinno zostać uruchomione), a jeśli nie ma onActivityResumed ( ) zadzwoń w ciągu najbliższych 3 sekund, zmień widoczny na false. Podobnie w onActivityResumed () jeśli istnieje licznik czasu, to go anuluję. Podsumowując, widoczne staje się isAppInBackground.

Niestety nie można skopiować i wkleić kodu ...

Sriram
źródło
-3

Chciałbym polecić Ci skorzystanie z innego sposobu, aby to zrobić.

Chyba chcesz pokazać ekran startowy podczas uruchamiania programu, jeśli jest już uruchomiony w backend, nie pokazuj go.

Twoja aplikacja może stale zapisywać aktualny czas w określonym pliku. Podczas uruchamiania aplikacji sprawdź ostatni znacznik czasu, jeśli bieżący_czas-ostatni_czas> zakres czasu określony przez Ciebie do zapisania ostatniego czasu, oznacza to, że aplikacja została zatrzymana, albo zabita przez system, albo przez samego użytkownika.

użytkownik2322866
źródło