Przez tło rozumiem, że żadna aktywność aplikacji nie jest obecnie widoczna dla użytkownika?
Przez tło rozumiem, że żadna aktywność aplikacji nie jest obecnie widoczna dla 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 implementacja Application
lub Service
(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ę 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 onPause
i onResume
do każdego Activity
w projekcie (możesz utworzyć wspólnego przodka dla swoich działań, jeśli chcesz, ale jeśli twoja aktywność jest już rozszerzona z MapActivity
/ ListActivity
itp., 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.
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ęRunningAppProcessInfo
rekordów. Aby ustalić, czy aplikacja znajduje się na pierwszym planie, zaznaczRunningAppProcessInfo.importance
pole równościRunningAppProcessInfo.IMPORTANCE_FOREGROUND
while, gdyRunningAppProcessInfo.processName
nazwa jest równa nazwie pakietu aplikacji.Również jeśli zadzwonisz
ActivityManager.getRunningAppProcesses()
z wątku interfejsu aplikacji, zwróci znaczenieIMPORTANCE_FOREGROUND
dla 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 przezAsyncTask
), 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.getRunningTasks
tej isApplicationBroughtToBackground
metody. Zobacz komentarz Dianne powyżej i nie używaj tej metody.
OnStop
zapytaniu do isActivityVisible
.
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.
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 MyLifecycleHandler
zostaną 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ć 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
„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()
.
onStop()
później super.onStop()
. Nie sprawdzaj tła w tle onPause()
.
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
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 dla multiple 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:
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 ProcessLifecycleOwner
dowolnym 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);
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;
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:
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
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
ani onResume
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.
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 ActivityBonStart()
zostanie wywołane przed ActivityAonStop()
.
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.xml
wewną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:
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()
.
if(Utilities.isApplicationOnBackground()) …
do Utilities
. 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ę.
Service
. Jeśli tak, poproś, aby twoje działania powiadomiły serwis w miarę ich pojawiania się i znikania. Jeśli Service
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łania MyActivityClass
. To nie zadziała PreferenceActivity
ani MapActivity
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
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
}
}
}
Opierając się na odpowiedzi @Cornstalks, zawiera kilka przydatnych funkcji.
Dodatkowe cechy:
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;
}
}
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ć.
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) { }
}
Jedyne prawidłowe rozwiązanie:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
MyApp.mainActivity = this;
super.onCreate(savedInstanceState);
...
}
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) {
...
}
}
}
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?
Application
nie ma onPause()
lub onResume()
.
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.
Ponieważ nie zostało to jeszcze wspomniane, zasugeruję czytelnikom zbadanie ProcessLifecycleOwner dostępnego za pośrednictwem komponentów architektury Android
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.
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();
}
}
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++;
}
}
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ść” RunningAppProcessInfo
klasy w onStop
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 implementuje onStop
metodę 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;
}
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.
onStop
; pomiędzy onPause
i onStop
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.
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
a Activity
zwykle 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:
Activity
, Application
...Opis
Więc chcesz sprawdzić, czy któryś z nich Activity
jest obecnie na pierwszym planie. Zazwyczaj robisz to w klasie Service
lub w Application
klasie.
Oznacza to, że twoje Activity
obiekty stają się nadawcą sygnału (jestem włączony / jestem wyłączony). Twój Service
natomiast staje się Receiver
.
Są 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 Activity
na pierwszy plan onResume()
uruchamiana jest metoda (wywoływana również po onCreate()
).
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: *
Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON
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.
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ę Receiver
wService::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 Activity
muszą 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
Service
i zaktualizowaćActivity
. Jak sprawdzić, czyActivity
jest na pierwszym planie?
Zazwyczaj nie ma potrzeby sprawdzania, czy Activity
jest na pierwszym planie, czy nie. Po prostu wyślij dane za pośrednictwem LocalBroadcastManager
swojego Service
. Jeśli Activity
jest włączony, zareaguje i zadziała.
W tej bardzo powszechnej sytuacji Service
staje się nadawcą, a Activity
implementuje BroadcastReceiver
.
Więc stwórz Receiver
w swoim Activity
. Zarejestruj się onResume()
i wyrejestruj onPause()
. Nie ma potrzeby stosowania innych metod cyklu życia .
Zdefiniuj Receiver
zachowanie w onReceive()
(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
via Intent
(patrz kod powyżej), podając identyfikator sygnału.
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
}
Ż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ę, czy MyActivity
jest 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
}
}
}
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 ...
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 ...
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.