Как обрабатывать код, когда приложение убито смахиванием в Android?

105

Если мое приложение запущено, и я нажимаю кнопку «Домой», приложение переходит в фоновый режим. Теперь , если длительное нажатие на кнопку домой и убить приложение, ударяя его из списка последних приложений, ни одно из событий не нравится onPause(), onStop()или onDestroy()вызывается , а процесс завершается. Итак, если я хочу, чтобы мои службы остановились, отключили уведомления и отменили регистрацию слушателей, как я могу это сделать? Я прочитал довольно много статей и блогов, но не получил никакой полезной информации и не нашел никакой документации по этому поводу. Любая помощь будет оценена. Заранее спасибо.

CodeWarrior
источник
Я решаю свою проблему. Проверить
MysticMagicϡ
@MysticMagic Это работает для службы, как насчет отмены уведомлений и отмены регистрации слушателей?
CodeWarrior
Вы можете отменить регистрацию слушателей в onTaskRemoved. А ты не можешь?
То
@ MysticMagicϡ Что, если я хочу сделать то же самое при обновлении приложения?
reji

Ответы:

151

Я только что решил подобную проблему.

Вот что вы можете сделать, если просто остановите службу, когда приложение будет убито, проведя пальцем по списку последних приложений.

Внутри вашего файла манифеста оставьте флаг stopWithTaskкак trueдля службы. Подобно:

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

Но поскольку вы говорите, что хотите отменить регистрацию слушателей и остановить уведомление и т. Д., Я бы предложил такой подход:

  1. Внутри вашего файла манифеста оставьте флаг 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();  
    }
    

Обратитесь к моему вопросу для получения более подробной информации, который также содержит другую часть кода.

Надеюсь это поможет.

MysticMagicϡ
источник
Спасибо. Думаю, можно ли использовать этот метод для выявления убийств смахивания. Простая пустая услуга?
Kiran
Да, @Kiran, ты можешь попробовать. :)
MysticMagicϡ
1
@ MysticMagicϡ привет, один вопрос, я пробовал использовать ваше решение, но кажется, что длительные операции, такие как отправка данных на сервер, аналитика в Google и т. Д., Иногда не проходят полностью. Может быть, процесс был убит до того, как этот метод полностью завершился? Вы также пробовали этот метод на устройстве Huawei? Кажется, что onTaskRemoved никогда не вызывается на этих устройствах. Есть идеи, почему?
Алон Мински
2
@ MysticMagicϡ public void onTaskRemoved (..) Метод не вызывается в Android O. Работает нормально в других ОС, но возникают проблемы в Android O.
Джей Патель,
2
Этот код не работает для Android O из-за ограничения фоновой службы. Есть ли способ справиться с этим на всех устройствах Android?
Aanal Mehta,
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 () будет выполняться.

Emilpmp
источник
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 без service.onTaskRemoved () - отлично сделано
Gal Rom
4
Это решение только определяет, было ли приложение перезапущено, но вы не можете определить точный момент, когда приложение было убито,
Вадим Котов,
6

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

Пустыня
источник
12
Спасибо за предложение, но я не хочу убивать свои уведомления, службы и слушателей, onPause()и onstop()их нужно убивать только тогда, когда пользователь выходит из приложения, то есть выходит из системы.
CodeWarrior
1

Вам нужно сохранить свои данные, когда onPause()вызывается. Взгляните на эту диаграмму жизненного цикла: Android Developer

Вы можете видеть, что приложение можно убить после onPause()или onStop().

Храните там свои данные и восстанавливайте их в onRestart()\ onCreate().

удачи!

Фашизель
источник
1

Вы не можете обрабатывать смахивание, потому что система просто удаляет ваш процесс из памяти, не вызывая никакого обратного вызова.

Я проверил, что перед тем, как пользователь вызовет экран «Недавние приложения», всегда будет вызываться onPause (). Поэтому вам нужно сохранить все данные в методе onPause, не проверяя isFinishing ().

Чтобы проверить кнопку возврата, используйте метод onBackPressed.

Ахмад
источник
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 8, верно?
rahil008
0

Как упомянул Боб Крам в своем ответе, ответом является метод onCleared () модели View . Работает в обоих случаях:

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

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

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

 override fun onCleared() {
        super.onCleared()
        Log.d(TAG , "App Killed")
    }
Уткарш Сингх
источник