Як обробити код, коли додаток вбито, провевши в Android?


104

Якщо мій додаток працює, і я натискаю кнопку додому, програма переходить у фоновий режим. Тепер , якщо тривале натискання на кнопку додому і вбити додаток, б'ючи його зі списку останніх програм, жодна з подій не подобається onPause(), onStop()або onDestroy()викликається , а процес завершується. Тож якщо я хочу, щоб мої служби припинялися, вбивали сповіщення та скасовували реєстрацію слухачів, як це зробити? Я прочитав досить багато статей та блогів, але не отримав корисної інформації, і не знайшов жодної документації про це. Будь-яка допомога буде вдячна. Заздалегідь спасибі.


Я вирішую свою проблему. Перевірка
MysticMagicϡ

@MysticMagic Це працює для Сервісу, а як скасувати сповіщення та скасувати реєстрацію слухачів?
CodeWarrior

Ви можете скасувати реєстрацію слухачів у програмі onTaskRemoved. Ти не можеш? І те саме для скасування сповіщень
MysticMagicϡ

@ MysticMagicϡ Що робити, якщо я хочу робити те ж саме, що стосується оновлення програми?
reji

Відповіді:


150

Я просто вирішив подібне питання.

Ось що ви можете зробити, якщо мова йде лише про зупинку служби, коли програму вбивають, перетягуючи його зі списку Останні програми.

Всередині файлу Manifest зберігайте прапор stopWithTaskяк trueдля служби. Подібно до:

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

Але, як ви кажете, що хочете скасувати реєстрацію слухачів і припинити повідомлення тощо, я б запропонував такий підхід:

  1. Всередині файлу Manifest зберігайте прапор stopWithTaskяк falseдля служби. Подібно до:

    <service
        android:name="com.myapp.MyService"
        android:stopWithTask="false" />
  2. Тепер у ваших MyServiceпослугах метод переосмислення onTaskRemoved. (Це буде запущено, лише якщо stopWithTaskвстановлено false).

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

Для отримання детальної інформації зверніться до мого питання , яке містить і іншу частину коду.

Сподіваюся, це допомагає.


Дякую. Подумайте, чи можна цей метод використати для виявлення вбивства, що протікає. Простий пустий сервіс?
Кіран

Так, @Kiran, можна спробувати. :)
MysticMagicϡ

1
@ MysticMagicϡ Привіт, одне питання, я спробував використовувати ваше рішення, але, здається, тривалі операції, такі як пересилання даних на сервер, аналітика в google і т. Д., Іноді не проходять весь шлях. Чи може бути, що процес вбито до того, як цей метод мав шанс повністю завершитись? Ви також спробували цей метод на пристрої Huawei? Здається, що onTaskRemoved ніколи не викликається на цих пристроях. Будь-які ідеї чому?
Алон Мінський

2
@ MysticMagicϡ public void onTaskRemoved (..) Метод не викликати в Android O. Працює нормально в інших ОС, але отримує проблему в Android O.
Джей Патель,

2
Цей код не працює для Android O через обмеженість фонового обслуговування. Чи є спосіб вирішити це на всіх пристроях Android?
Аанал ​​Мехта

33

Знайшов один із способів зробити це

зробити одну послугу подібною

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) зареєструйте цю послугу в manifest.xml

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

3) Потім запустіть цю послугу зі своєї дії

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

І тепер, коли ви очистите свою програму від Android нещодавно, тоді цей метод onTaskRemoved () виконає.


1
Один до одного скопіювати і вставити з цієї відповіді: stackoverflow.com/questions/21040339
Flot2011

17

Я вирішив подібне питання. Якщо ви хочете, після того, як ви перейдете з останнього завдання та при наступному запуску, він буде вести себе належним чином, виконайте наведені нижче дії:

1) Збережіть ідентифікатор процесу у спільних налаштуваннях:

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

2) Коли програма запускається із запуску після очищення від останнього завдання, тоді виконайте:

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
}

Підтверджено - використовується на Android 8 без служби.onTaskRemoved () - прекрасно зроблено
Gal Rom

4
Це рішення виявляє лише те, що програма перезапущена, але ви не можете визначити точний момент, коли програму було вбито
Вадим Котов

6

Коли ви натискаєте додому - onPauseі onStopваша активність викликається, тому в цей час вам доведеться робити всі заощадження та очищення, оскільки платформа Android більше не гарантує, що onDestroyбудь-який інший спосіб життєвого циклу буде викликаний, тому процес може бути знищений без будь-якого повідомлення.


12
Дякую за пропозицію, але я не хочу вбивати свої сповіщення, служби та слухачів, onPause()і onstop()їх потрібно вбивати лише тоді, коли користувач закриває додаток, тобто вийшов із системи.
CodeWarrior

1

Вам потрібно зберегти свої дані, коли onPause()подзвонено. Подивіться на цю схему життєвого циклу: Android Developer

Ви можете бачити, що додаток може бути вбито після onPause()або onStop().

Обробіть свої дані там і відновіть їх у onRestart()\ onCreate().

Щасти!


1

Ви не можете працювати з пальцем, оскільки система просто видаляє ваш процес із пам'яті, не викликаючи зворотного дзвінка.

Я перевірив, що перед тим, як користувач викликає екран "останніх додатків", OnPause () буде завжди викликаний. Отже, вам потрібно зберегти всі дані в методі onPause, не перевіряючи isFinishing ().

Щоб перевірити кнопку назад, використовуйте метод onBackPress.


1
Я вважаю, що це єдиний безпечний метод для всіх версій Android.
Серхіо

@Sergio Так, це найбезпечніший метод, і я перевірив його в масштабному застосуванні.
Ахмад

1

ViewModel.onCleared () може бути корисним, якщо метою є звільнення деякого ресурсу (можливо, система, що працює десь в мережі), коли користувач виконує сюрпризний вихід, провівши, а не натискаючи кнопку "стоп" або кнопку. [Ось як я спочатку прийшов до цього питання].

Програма не отримує сповіщення, і Activity.onDestroy () викликає зміни конфігурації, такі як зміни орієнтації, тому відповіді немає. Але ViewModel.onCleared отримує виклик, коли додаток розгорнуто (як і коли користувач відмовляється від активності). Якщо ресурс, який ви хочете використовувати, пов'язаний з більш ніж однією діяльністю в стеці, ви можете додати підрахунок посилань або інший механізм, щоб вирішити, чи повинен ViewModel.onClear звільнити ресурс.

Це ще одна з багатьох вагомих причин використовувати ViewModel.

- Боб


1

Я не знаю, чому вищезазначені підходи не працюють у моєму випадку, навіть я встановив, android:stopWithTask="false"що onTaskRemoved()не закликав.

Ще одним хорошим підходом було б використання AndroidViewModel. Цей навіть працює у випадку, коли користувач закриває додаток натисканням кнопки "назад".

Просто прив’яжіть ViewModelклас до свого, MainActivityтоді виконуйте onCleared()зворотний виклик завдань .

Приклад:

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

потім прив’яжіть його до вашої MainActivity:

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

~ Вуаля!


можливо, це не працює з підходом до служби через Android O і вище, як згадували інші коментатори. Це здається цікавим рішенням для використання моделей перегляду для цього. Чи все-таки це спрацьовує, якщо програма забита силою (проведіть пальцем на екрані перемикача програм)? редагувати: Відповідь Боба говорить, що це працює в цих випадках
Вільям Рід

так, це працює, тому справа, з якою я зіткнувся
droidhs

0

Це працювало для мене на android 6,7,8,9.

Зробіть одну послугу так:

 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) Зареєструйте цю послугу у manifest.xml:

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

3) Потім запустіть цю послугу зі своєї дії

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

1
І чому ваш сервіс буде продовжувати працювати на Android 8 і вище? Ви розумієте, що для Android Star 8 доступні обмеження, чи не так?
rahil008

0

Як згадував Боб Крам у своїй відповіді, метод відповіді onCleared () View Model є відповіддю. Він працює в обох випадках:

  1. Коли користувач видаляє додаток, перетягнувши програму з фону.
  2. Коли користувач очистить усе додаток за допомогою кнопки очищення списку.

Служба onTaskRemoved () працюватиме, коли користувач переводить програму з фону, але не працює, коли програми очищаються за допомогою кнопки знищити все.

Але метод onCleared () viewModel працює в обох випадках. Ви можете використовувати, якщо зупинити будь-який поточний процес або очистити будь-яке завдання на віддаленому сервері.

 override fun onCleared() {
        super.onCleared()
        Log.d(TAG , "App Killed")
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.