Jak obsłużyć kod, gdy aplikacja jest zabijana przez przeciągnięcie w systemie Android?

104

Jeśli moja aplikacja jest uruchomiona i naciskam przycisk ekranu głównego, aplikacja działa w tle. Teraz, jeśli długie naciśnięcie przycisku home i zabić aplikację, przesuwając go z niedawnym liście aplikacji, żaden z wydarzeń podoba onPause(), onStop()czy onDestroy()pobiera nazywane raczej proces jest zakończony. Jeśli więc chcę, aby moje usługi zatrzymywały się, wyłączały powiadomienia i wyrejestrowywały słuchaczy, jak mogę to zrobić? Przeczytałem sporo artykułów i blogów, ale nie znalazłem żadnych przydatnych informacji i nie znalazłem na ten temat żadnej dokumentacji. Każda pomoc będzie mile widziana. Z góry dziękuję.

CodeWarrior
źródło
Rozwiązuję swój problem. Sprawdź
MysticMagicϡ
@MysticMagic To działa dla usługi, a co z anulowaniem powiadomień i wyrejestrowaniem słuchaczy?
CodeWarrior
Możesz wyrejestrować słuchaczy w onTaskRemoved. Nie możesz? To samo dotyczy anulowania powiadomień
MysticMagicϡ
@ MysticMagicϡ Co zrobić, jeśli chcę zrobić to samo przy aktualizacji aplikacji?
reji

Odpowiedzi:

150

Właśnie rozwiązałem podobny problem.

Oto, co możesz zrobić, jeśli chodzi tylko o zatrzymanie usługi, gdy aplikacja zostanie zabita, przesuwając palcem z listy ostatnich aplikacji.

W pliku Manifest zachowaj flagę stopWithTaskjak truedla usługi. Lubić:

<service
    android:name="com.myapp.MyService"
    android:stopWithTask="true" />

Ale skoro mówisz, że chcesz wyrejestrować słuchaczy i zatrzymać powiadomienia itp., Proponuję następujące podejście:

  1. W pliku Manifest zachowaj flagę stopWithTaskjak falsedla usługi. Lubić:

    <service
        android:name="com.myapp.MyService"
        android:stopWithTask="false" />
  2. Teraz do Twojej MyServiceusługi, zastąp metodę onTaskRemoved. (Zostanie stopWithTaskuruchomiony tylko wtedy, gdy jest ustawiony na false).

    public void onTaskRemoved(Intent rootIntent) {
    
        //unregister listeners
        //do any other cleanup if required
    
        //stop service
        stopSelf();  
    }

Odnieś się do mojego pytania, aby uzyskać więcej informacji, które zawiera również inną część kodu.

Mam nadzieję że to pomoże.

MysticMagicϡ
źródło
Dzięki. Zastanawianie się, czy ta metoda może być użyta do identyfikacji zabójstw przez machnięcie. Prosta usługa pusta?
Kiran
Tak @Kiran, możesz spróbować. :)
MysticMagicϡ
1
@ MysticMagicϡ Cześć, jedno pytanie, próbowałem użyć twojego rozwiązania, ale wydaje się, że długie operacje, takie jak wysyłanie danych na serwer, analizy do Google itp., Czasami nie udają się do końca. Czy to możliwe, że proces został zabity, zanim ta metoda miała szansę się w pełni zakończyć? Czy wypróbowałeś tę metodę na urządzeniu Huawei? Wygląda na to, że onTaskRemoved nigdy nie jest wywoływany na tych urządzeniach. Jakieś pomysły, dlaczego?
Alon Minski
2
@ MysticMagicϡ public void onTaskRemoved (..) Metoda nie wywołuje w systemie Android O. Działa dobrze w innym systemie operacyjnym, ale pojawia się problem w systemie Android O.
Jay Patel
2
Ten kod nie działa na Androidzie O z powodu ograniczenia usługi w tle. Czy istnieje sposób na rozwiązanie tego problemu na wszystkich urządzeniach z Androidem?
Aanal Mehta
33

Znalazłem jeden sposób, aby to zrobić

zrobić jedną taką usługę

public class OnClearFromRecentService extends Service {

@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d("ClearFromRecentService", "Service Started");
    return START_NOT_STICKY;
}

@Override
public void onDestroy() {
    super.onDestroy();
    Log.d("ClearFromRecentService", "Service Destroyed");
}

@Override
public void onTaskRemoved(Intent rootIntent) {
    Log.e("ClearFromRecentService", "END");
    //Code here
    stopSelf();
}

}

2) zarejestruj tę usługę w pliku manifest.xml

<service android:name="com.example.OnClearFromRecentService" android:stopWithTask="false" />

3) Następnie uruchom tę usługę na swojej aktywności splash

startService(new Intent(getBaseContext(), OnClearFromRecentService.class));

A teraz za każdym razem, gdy wyczyścisz swoją aplikację z androida latest Następnie ta metoda onTaskRemoved () zostanie wykonana.

emilpmp
źródło
1
Jeden do jednego skopiuj i wklej z tej odpowiedzi: stackoverflow.com/questions/21040339
Flot2011
17

Rozwiązałem podobny problem. Jeśli chcesz, aby po przesunięciu z ostatniego zadania i następnym uruchomieniu zachowywało się poprawnie, wykonaj poniższe czynności: -

1) Zapisz identyfikator procesu we wspólnych preferencjach:

SharedPreferencesUtils.getInstance().putInt(SharedPreferencesUtils.APP_PROCESS_ID, android.os.Process.myPid());

2) Gdy aplikacja jest uruchamiana z programu uruchamiającego po usunięciu ostatniego zadania, wykonaj następujące czynności:

int previousProcessID = mSharedPreferencesUtils.getInt(SharedPreferencesUtils.APP_PROCESS_ID);

int currentProcessID = android.os.Process.myPid();

if ((previousProcessID == currentProcessID)) {
    // This ensures application not killed yet either by clearing recent or anyway
} else {
    // This ensures application killed either by clearing recent or by anyother means
}
Anshul Agarwal
źródło
Potwierdzone - używane na Androidzie 8 bez usługi.onTaskRemoved () - ładnie zrobione
Gal Rom
4
Rozwiązanie to wykrywa tylko wtedy, gdy aplikacja została wznowiona, ale nie można wykryć dokładny moment, kiedy aplikacja została zabita
Vadim Kotov
6

Kiedy naciskasz do domu - onPausei onStopTwoja aktywność jest wywoływana, więc w tej chwili musisz dokonać wszystkich oszczędności i wyczyścić, ponieważ platforma Android nie gwarantuje dalej, że onDestroyzostanie wywołana żadna inna metoda cyklu życia, więc proces może zostać zabity bez powiadomienia.

Pustynia
źródło
12
Dziękuję za sugestię, ale nie chcę wyłączać moich powiadomień, usług i słuchaczy, włączonych onPause()i onstop()trzeba je zabijać tylko wtedy, gdy użytkownik wyjdzie z aplikacji, tj. Wyloguje się.
CodeWarrior,
1

Musisz zapisać swoje dane, gdy ktoś onPause()dzwoni. Spójrz na ten diagram cyklu życia: Android Developer

Możesz zobaczyć, że aplikację można zabić po onPause()lub onStop().

Przetwórz tam swoje dane i odzyskaj je w onRestart()\ onCreate().

powodzenia!

Fashizel
źródło
1

Nie możesz sobie poradzić ze swipe, ponieważ system po prostu usuwa twój proces z pamięci bez wywoływania żadnego oddzwaniania.

Sprawdziłem, że przed wywołaniem przez użytkownika ekranu „ostatnie aplikacje” zawsze będzie wywoływana funkcja onPause (). Musisz więc zapisać wszystkie dane w metodzie onPause bez sprawdzania isFinishing ().

Aby sprawdzić przycisk wstecz, użyj metody onBackPressed.

Ahmad
źródło
1
Uważam, że jest to jedyna bezpieczna metoda dla wszystkich wersji Androida.
Sergio
@Sergio tak, to najbezpieczniejsza metoda i przetestowałem ją na większej skali aplikacji.
Ahmad
1

ViewModel.onCleared () może być przydatna, jeśli celem jest zwolnienie jakiegoś zasobu (na przykład systemu działającego w innym miejscu w sieci), gdy użytkownik wykonuje niespodziewane wyjście, przesuwając palcem, a nie naciskając przycisk „stop” lub przycisk. [W ten sposób pierwotnie doszedłem do tego pytania].

Aplikacja nie otrzymuje powiadomienia, a Activity.onDestroy () jest wywoływana w przypadku zmian konfiguracji, takich jak zmiany orientacji, więc nie ma odpowiedzi. Ale ViewModel.onCleared jest wywoływana, gdy aplikacja jest usuwana (a także gdy użytkownik wycofuje się z działania). Jeśli zasób, którego chcesz użyć, jest powiązany z więcej niż jednym działaniem na stosie, możesz dodać liczniki odwołań lub inny mechanizm, aby zdecydować, czy ViewModel.onClear powinien zwolnić zasób.

To kolejny z wielu dobrych powodów, dla których warto używać ViewModel.

- Bob

Bob Cram
źródło
1

Naprawdę nie wiem, dlaczego powyższe podejścia nie działają w moim przypadku, nawet jeśli ustawiłem, android:stopWithTask="false"że onTaskRemoved()nie wywołałem.

Innym dobrym podejściem byłoby użycie AndroidViewModel. Ten działa nawet w przypadku, gdy użytkownik wychodzi z aplikacji po naciśnięciu przycisku Wstecz.

Po prostu połącz ViewModelklasę ze swoją, MainActivitya następnie wykonaj onCleared()wywołanie zwrotne zadania .

Przykład:

public class MainViewModel extends AndroidViewModel {
    public MainViewModel(@NonNull Application application) {
        super(application);
    }

    @Override
    protected void onCleared() {
        // Do your task here
        Log.e("MainViewModel", "OnCleared mainViewModel");
        super.onCleared();
    }
}

następnie powiąż go z MainActivity:

MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class);

~ Voila!

droidhs
źródło
może nie działać z podejściem usługowym z powodu Androida O i powyżej, o czym wspominali inni komentatorzy. Wydaje się, że jest to interesujące rozwiązanie do wykorzystania w tym celu modeli widoku. Czy to nadal jest wyzwalane, jeśli aplikacja zostanie zabita na siłę (przesunięcie na ekranie przełącznika aplikacji)? edycja: odpowiedź Boba sugeruje, że działa w tych przypadkach
William Reed
tak to działa, to jest przypadek z którym się spotkałem
droidhs
0

To działało dla mnie na Androidzie 6,7,8,9.

Wykonaj jedną usługę w ten sposób:

 public class OnClearFromRecentService extends Service {

 @Override public IBinder onBind(Intent intent) {
     return null; }

 @Override public int onStartCommand(Intent intent, int flags, int
 startId) {
     Log.d("ClearFromRecentService", "Service Started");
     return START_NOT_STICKY; }

 @Override public void onDestroy() {
     super.onDestroy();
     Log.d("ClearFromRecentService", "Service Destroyed"); }

 @Override public void onTaskRemoved(Intent rootIntent) {
     Log.e("ClearFromRecentService", "END");
     //Code here
     stopSelf(); } }

2) Zarejestruj tę usługę w manifest.xml:

<service android:name="com.example.OnClearFromRecentService"
 android:stopWithTask="false" />

3) Następnie uruchom tę usługę na swojej aktywności splash

 startService(new Intent(getBaseContext(),
 OnClearFromRecentService.class));
Alexis Osorio
źródło
1
Dlaczego Twoja usługa będzie nadal działać na Androidzie 8 i nowszych? Zdajesz sobie sprawę, że istnieją ograniczenia dotyczące uruchomionych usług po Androidzie 8, prawda?
rahil008
0

Jak wspomniał Bob Cram w swojej odpowiedzi, odpowiedzią jest metoda onCleared () View Model . Działa w obu przypadkach:

  1. Gdy użytkownik usuwa aplikację, przesuwając ją z tła.
  2. Gdy użytkownik wyczyści całą aplikację za pomocą przycisku czyszczenia listy.

Funkcja onTaskRemoved () usługi będzie działać, gdy użytkownik przesunie aplikację z tła, ale nie będzie działać, gdy aplikacje zostaną wyczyszczone za pomocą przycisku zabicia wszystkiego.

Ale metoda onCleared () viewModel działa w obu przypadkach. Możesz użyć if, aby zatrzymać trwający proces lub wyczyścić dowolne zadanie na serwerze zdalnym.

 override fun onCleared() {
        super.onCleared()
        Log.d(TAG , "App Killed")
    }
Utkarsh Singh
źródło