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. : /
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:
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ą):
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):
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.
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.
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.
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 ...
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:
publicclassMyLifecycleHandlerimplementsActivityLifecycleCallbacks{// 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.privateint resumed;privateint paused;privateint started;privateint stopped;@Overridepublicvoid onActivityCreated(Activity activity,Bundle savedInstanceState){}@Overridepublicvoid onActivityDestroyed(Activity activity){}@Overridepublicvoid onActivityResumed(Activity activity){++resumed;}@Overridepublicvoid onActivityPaused(Activity activity){++paused;
android.util.Log.w("test","application is in foreground: "+(resumed > paused));}@Overridepublicvoid onActivitySaveInstanceState(Activity activity,Bundle outState){}@Overridepublicvoid onActivityStarted(Activity activity){++started;}@Overridepublicvoid 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" ...publicclassMyApplicationextendsApplication{@Overridepublicvoid 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(newMyLifecycleHandler());}}
@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 posuper.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 posuper.onStop() .
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().
classArchLifecycleApp: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:
publicclassArchLifecycleAppextendsApplicationimplementsLifecycleObserver{@Overridepublicvoid onCreate(){super.onCreate();ProcessLifecycleOwner.get().getLifecycle().addObserver(this);}@OnLifecycleEvent(Lifecycle.Event.ON_STOP)publicvoid onAppBackgrounded(){//App in background}@OnLifecycleEvent(Lifecycle.Event.ON_START)publicvoid 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'}}}
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 backgroundProcessLifecycleOwner.get().getLifecycle().getCurrentState()==Lifecycle.State.CREATED;//Check if app is in foregroundProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
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).
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.
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 ?!
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żdegoActivity 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.
publicclassUtilitiesextendsApplication{privatestaticint stateCounter;publicvoid onCreate(){super.onCreate();
stateCounter =0;}/**
* @return true if application is on background
* */publicstaticboolean isApplicationOnBackground(){return stateCounter ==0;}//to be called on each Activity onStart()publicstaticvoid activityStarted(){
stateCounter++;}//to be called on each Activity onStop()publicstaticvoid 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:
@Overridepublicvoid onStart(){super.onStart();Utilities.activityStarted();}@Overridepublicvoid 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:
stateCounter = 0 : Liczba zatrzymanych jest równa liczbie rozpoczętych działań, co oznacza, że aplikacja działa w tle.
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().
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ę.
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
publicclassMainActivityextendsAppCompatActivityimplementsComponentCallbacks2{@Overridepublicvoid onConfigurationChanged(finalConfiguration newConfig){}@Overridepublicvoid onLowMemory(){}@Overridepublicvoid onTrimMemory(finalint level){if(level ==ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){// app is in background}}}
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)
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ć.
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:
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?
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ń:
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.
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:
Ma widoczną aktywność, niezależnie od tego, czy aktywność została uruchomiona, czy wstrzymana.
Ma usługę pierwszego planu.
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.
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.
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:
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 ()?
this.localBroadcastReceiver =newBroadcastReceiver(){@Overridepublicvoid onReceive(Context context,Intent intent){// received data if Activity is on / off}}publicstaticfinalIntentFilter SIGNAL_FILTER =newIntentFilter("com.you.yourapp.MY_SIGNAL")
@Overrideprotectedvoid 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 =newIntent();
intent.setAction(SomeActivity.SIGNAL_FILTER);// put ON boolean in intent LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
W Activity::onPause()
Intent intent =newIntent();
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) ...
fun isAppInForeground():Boolean{
val activityManager = getSystemService(Context.ACTIVITY_SERVICE)asActivityManager?:returnfalse
val appProcesses = activityManager.runningAppProcesses ?:returnfalse
val packageName = packageName
for(appProcess in appProcesses){if(appProcess.importance ==ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName){returntrue}}returnfalse}
Ż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.
classMyActivityMonitor(context:Context):Application.ActivityLifecycleCallbacks{privatevar isMyActivityInForeground =false
init {(context.applicationContext asApplication).registerActivityLifecycleCallbacks(this)}
fun isMyActivityForeground()= isMyActivityInForeground
override fun onActivityPaused(activity:Activity?){if(activity isMyActivity){
isMyActivityInForeground =false}}override fun onActivityResumed(activity:Activity?){if(activity isMyActivity){
isMyActivityInForeground =true}}
// 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 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.
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.
Odpowiedzi:
Istnieje kilka sposobów na wykrycie, czy aplikacja działa w tle, ale tylko jeden z nich jest całkowicie niezawodny:
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.onResume
metod. Przechowuj status „widoczności” w innej klasie. Dobrym wyborem jest Twoja własna implementacjaApplication
lubService
(istnieje również kilka odmian tego rozwiązania, jeśli chcesz sprawdzić widoczność aktywności z usługi).Przykład
Implementacja niestandardowej
Application
klasy (zwróć uwagę naisActivityVisible()
metodę statyczną):Zarejestruj swoją klasę aplikacji w
AndroidManifest.xml
:Dodaj
onPause
ionResume
do każdegoActivity
w projekcie (możesz utworzyć wspólnego przodka dla swoich działań, jeśli chcesz, ale jeśli twoja aktywność jest już rozszerzona zMapActivity
/ListActivity
itp., Nadal musisz ręcznie napisać następujące):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.
Zły
użyłem, aby zasugerować następujące rozwiązanie:
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 :
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.
Kolejne błędne rozwiązanie biblioteka
Droid-Fu wspomniana w jednej z odpowiedzi używa
ActivityManager.getRunningTasks
tejisApplicationBroughtToBackground
metody. Zobacz komentarz Dianne powyżej i nie używaj tej metody.źródło
OnStop
zapytaniu doisActivityVisible
.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ś wonResumed()
/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:
MyApplication.java:
@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ą: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
MyLifecycleHandler
zostaną zresetowane do0
.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ćconfigChanges
tak, 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
„sonPause()
metody posuper.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
„sonStop()
metody posuper.onStop()
.źródło
onStop()
późniejsuper.onStop()
. Nie sprawdzaj tła w tleonPause()
.ROZWIĄZANIE GOOGLE - nie hack, jak poprzednie rozwiązania. Użyj ProcessLifecycleOwner
Kotlin:
Jawa:
w app.gradle
Możesz przeczytać więcej o komponentach architektury związanych z cyklem życia tutaj - https://developer.android.com/topic/libraries/architecture/lifecycle
źródło
companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }
wtedy możesz uzyskać stan pierwszego planu za pomocąArchLifecycleApp.isForeground()
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 dlamultiple processes
aplikacji, czy jest jakiś interfejs API, który możemy elegancko osiągnąć?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:
A potem po prostu zapytaj w
ProcessLifecycleOwner
dowolnym momencie o stan aplikacji, przykłady:źródło
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).
źródło
JobService
celu wykrycia, że usługa działa w tle.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:
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
źródło
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.onPause
,onStop
anionResume
wydarzeniem jest tzw. Co zatem robisz, jeśli żadne z tych zdarzeń nie zostanie uruchomione ?!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.
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żdegoActivity
programu musisz przesłonićonStart()
ionStop()
, 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ę rozszerzaApplication
, więc nie zapomnij zadeklarowaćManifest.xml
wewnątrz znacznika aplikacji:android:name=".Utilities"
chociaż można go zaimplementować również za pomocą prostej klasy niestandardowej.Teraz na każdej działalności naszego programu, należy nadpisać
onStart()
ionStop()
i góra / dół, jak pokazano poniżej:Przy tej logice istnieją 2 możliwe przypadki:
stateCounter = 0
: Liczba zatrzymanych jest równa liczbie rozpoczętych działań, co oznacza, że aplikacja działa w tle.stateCounter > 0
: Liczba uruchomionych jest większa niż liczba zatrzymanych, co oznacza, że aplikacja działa na pierwszym planie.Uwaga:
stateCounter < 0
oznaczał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()
.źródło
if(Utilities.isApplicationOnBackground()) …
doUtilities
. Ponieważ w przeciwnym razie tylko określone działanie zareaguje na zdarzenie.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ę.
źródło
Service
. Jeśli tak, poproś, aby twoje działania powiadomiły serwis w miarę ich pojawiania się i znikania. JeśliService
stwierdzi, ż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.MyActivityClass
dziedziczącąActivity
i wdrażającą metody cyklu życia, a także dziedziczyć wszystkie swoje działaniaMyActivityClass
. To nie zadziałaPreferenceActivity
aniMapActivity
nie (patrz to pytanie )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
,service
itpźródło
Opierając się na odpowiedzi @Cornstalks, zawiera kilka przydatnych funkcji.
Dodatkowe cechy:
App.java
AppLifecycleHandler.java
źródło
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ć.
źródło
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:
źródło
Jedyne prawidłowe rozwiązanie:
MainActivity.java:
MyApp.java:
źródło
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?
źródło
Application
nie maonPause()
lubonResume()
.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ń:
Po drugie, zaimplementowałem ten interfejs w klasie mojej aplikacji:
Po trzecie, tworzę klasę, która opiera się na SherlockActivity:
Po czwarte, wszystkie klasy należące do SherlockActivity zastąpiłem MySherlockActivity:
Teraz w logcat zobaczysz dzienniki zaprogramowane w implementacji interfejsu wykonanej w MyApplication.
źródło
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.
źródło
Ponieważ nie zostało to jeszcze wspomniane, zasugeruję czytelnikom zbadanie ProcessLifecycleOwner dostępnego za pośrednictwem komponentów architektury Android
źródło
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:
Jeśli żaden z tych warunków nie jest spełniony, aplikacja jest uznawana za działającą w tle.
źródło
Inne rozwiązanie dla tego starego postu (dla tych, które mogą pomóc):
źródło
Zobacz komentarz w funkcji onActivityDestroyed.
Działa z docelową wersją zestawu SDK 14>:
źródło
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.
źródło
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ść”
RunningAppProcessInfo
klasy wonStop
metodzie każdej czynności w mojej aplikacji, co można po prostu osiągnąć, udostępniając opcjęBaseActivity
rozszerzenia innych działań, która implementujeonStop
metodę sprawdzania wartości „ważności”. Oto kod:źródło
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.źródło
onStop
; pomiędzyonPause
ionStop
jest widoczny , ale nie na pierwszym planie .onStop()
wywołaniu, co jest zgodne z tym, co napisałeś.onPause
nie nazywa: ostatnia edycja cię poprawiła.Co z użyciem getApplicationState (). IsInForeground ()?
źródło
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,
Service
aActivity
zwykle radzę skorzystać z LocalBroadcastManager .Dlaczego?
Cytując dokumenty:
Nie w dokumentacji:
Activity
,Application
...Opis
Więc chcesz sprawdzić, czy któryś z nich
Activity
jest obecnie na pierwszym planie. Zazwyczaj robisz to w klasieService
lub wApplication
klasie.Oznacza to, że twoje
Activity
obiekty stają się nadawcą sygnału (jestem włączony / jestem wyłączony). TwójService
natomiast staje sięReceiver
.Są dwa momenty, w których twój
Activity
mówi ci, czy idzie na pierwszym planie, czy w tle (tak, tylko dwa ... nie 6).Po przejściu
Activity
na pierwszy planonResume()
uruchamiana jest metoda (wywoływana również poonCreate()
).Gdy
Activity
idzie z tyłu,onPause()
nazywa się.To są momenty, w których
Activity
powinieneś wysłać sygnał,Service
aby 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: *
Service
/Application
Po prostu zachować słuchanie tych sygnałów i podjąć odpowiednie działania.Kod (TLDR)
Twój
Service
musi implementowaćBroadcastReceiver
aby słuchać sygnałów.Zarejestruj się
Receiver
wService::onCreate()
Wyrejestruj go w
Service::onDestroy()
Teraz wasze
Activity
muszą zakomunikować swój stan.W
Activity::onResume()
W
Activity::onPause()
Bardzo, bardzo powszechna sytuacja
Zazwyczaj nie ma potrzeby sprawdzania, czy
Activity
jest na pierwszym planie, czy nie. Po prostu wyślij dane za pośrednictwemLocalBroadcastManager
swojegoService
. JeśliActivity
jest włączony, zareaguje i zadziała.W tej bardzo powszechnej sytuacji
Service
staje się nadawcą, aActivity
implementujeBroadcastReceiver
.Więc stwórz
Receiver
w swoimActivity
. Zarejestruj sięonResume()
i wyrejestrujonPause()
. Nie ma potrzeby stosowania innych metod cyklu życia .Zdefiniuj
Receiver
zachowanie wonReceive()
(zaktualizuj ListView, zrób to, zrób to ...).W ten sposób
Activity
bę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
Service
viaIntent
(patrz kod powyżej), podając identyfikator sygnału.źródło
źródło
Ż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
ActivityLifecycleCallbacks
tego, jak zalecono w innych odpowiedziach, stworzyłem małą klasę użytkową, która zawiera logikę, czyMyActivity
jest na pierwszym planie, czy nie.}
źródło
W moich działaniach onResume i onPause piszę wartość logiczną isVisible do SharedPrefences.
W razie potrzeby przeczytaj go gdzie indziej,
Może nie elegancki, ale działa dla mnie ...
źródło
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 ...
źródło
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.
źródło