Планировщик заданий и фоновое обслуживание

У меня есть приложение, которое имеет функцию А, которая должна запускаться в фоновом режиме каждую минуту. Функция A заключается в том, что приложение должно подключаться к базе данных, читать некоторые данные, затем получать текущее местоположение устройства и на их основе проверять условие, если условие выполняется, оно должно отправлять пользователю уведомление в строке состояния, чтобы, когда пользователь при нажатии на уведомление отобразится пользовательский интерфейс приложения, и что-то произойдет.
Эта фоновая задача должна выполняться постоянно каждую минуту, независимо от того, используется ли приложение, закрыто или прекращено (например, Facebook или WhatsApp, которые показывают нам уведомления, независимо от того, находятся они в стеке приложения или нет).
Теперь я искал и обнаружил, что Android предлагает планировщик заданий,фоновую службу, AlarmManager и обработчики.
Но чем больше я читаю о них, тем более противоречивыми кажутся мне высказывания.

  1. Об обработчиках я читал, что они не существуют для длительных задержек и будет прекращен после перезагрузки системы. Так они не будут подходит для моей задачи.
  2. Но AlarmManager, по-видимому, является хорошим кандидатом для решения этой проблемы, поскольку, если разрешено, они существуют даже после перезагрузки системы и могут перезапустите приложение. Но в Android-документации, что будильник Диспетчер предназначен для использования в задачах, которые должны быть запущены на конкретное время (например, будильник). Но моя задача должна быть выполнена каждую минуту.
  3. Тогда есть Фоновая служба. Это больше для задач, таких как загрузка в фоновом режиме, как я прочитал, и не предназначен для делать то, что я объяснил.
  4. Кажется, что JobScheduler предназначен не для задачи, которая должна выполняться постоянно, а для задач, которые удовлетворяют определенному ограничению, например простаивает или нет сети... Так что же из этих (или других, если они существует) вы рекомендуете использовать для задачи, которую я объяснил в первом часть

Ответ 1

У меня есть приложение, которое имеет функцию A, которая должна работать в фоновом режиме каждую минуту.

Это не произойдет на сотнях миллионов устройств Android, работающих под управлением Android 6.0 и выше, из-за режима Doze (и, возможно, ожидания приложения в зависимости от остальной части вашего приложения).

Но AlarmManager, похоже, является хорошим кандидатом на эту проблему, потому что, когда он разрешен, они существуют даже после перезагрузки системы

Нет, они этого не делают. После перезагрузки вам необходимо перенести все аварийные сигналы, запланированные с помощью AlarmManager.

Менеджер аварийных сообщений предназначен для задач, которые должны выполняться в определенное время

AlarmManager поддерживает повторяющиеся параметры.

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

A Service будет существенным для любого решения, которое вы используете.

JobScheduler, похоже, не предназначен для задачи, которая должна выполняться постоянно, но для задач, которые выполняют определенное ограничение, например, бездействия, или нет сети

JobScheduler, как и в AlarmManager, поддерживает повторяющиеся задания.

Итак, какие из них (или другие, если они существуют) вы рекомендуете использовать для задачи, которую я объяснил в первой части

Не используйте ни один из них, так как вы не можете запускать что-либо каждую минуту на Android 6.0+, как только устройство переходит в режим "Дозирование", который будет находиться в течение часа после выключения экрана. Вместо этого, либо переработайте приложение, либо потребуется только фоновая работа несколько раз в день, либо не документируйте приложение.

Ответ 2

Вы можете использовать современный API JobScheduler, который был представлен в Android 5.0, если ваш minSdkVersion = 21.

Также есть https://github.com/firebase/firebase-jobdispatcher-android, для которого требуется установленный Google Play minSdkVersion = 9

Но я рекомендую использовать эту библиотеку https://github.com/evernote/android-job, где в зависимости от версии Android будут использоваться JobScheduler, GcmNetworkManager или AlarmManager.

С помощью этих API вы можете планировать свою работу и запускать сервис, который описывает задачу.

UPDATE Теперь лучше использовать новый WorkManager (документы). android-job скоро будет устаревшим

Ответ 3

Во-первых, JobService - это Служба. Фоновая служба неоднозначна, позвольте мне предположить, что вы имеете в виду службу, которая работает в фоновом потоке. Служба Job работает в потоке ui, но вы можете создать в ней объект задачи async, чтобы он выполнялся в фоновом режиме.

Из вашего вопроса, JobService - это не путь. Что я предлагаю:

  • Вы можете создать класс, который расширяет IntentService (это выполняется в фоновом потоке) в методе onDestroy этого класса, отправляет широковещательную рассылку и перезапускает службу вещания.

     @onDestroy(){
     Intent broadcastIntent = new 
     Intent("com.example.myapp.serviceRestarted");
     sendBroadcast(broadcastIntent);}
    
  • Создайте класс, который расширяет приемник вещания

     public class RestartServiceReceiver extends BroadcastReceiver {
     @Override
     public void onReceive(Context context, Intent intent) {
     context.startService(new Intent(context, 
     MyService.class));
    } 
    }
    
    1. В своем манифесте зарегистрируйте свой сервис и ресивер
<receiver
            android:name=".RestartServiceReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.myapp.serviceRestarted" />
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

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

Ответ 4

Согласно this и другая ссылка в комментарии 1 ниже

Вы должны использовать AlarmManager для своей задачи.

Если вам нужно установить аварийные сигналы, которые срабатывают во время Doze, используйте:

 setAndAllowWhileIdle() or setExactAndAllowWhileIdle().

Для полного простого понимания объяснений различных способов сделать материал в фоновом режиме читайте: https://www.bignerdranch.com/blog/choosing-the-right-background-scheduler-in-android/

Удачи!

Ответ 5

В предыдущих версиях Android люди использовали Handler или фоновые службы для этой цели. Через некоторое время они объявили класс аварийного менеджера для постоянных, запланированных работ.

Whatsapp, facebook или некоторые приложения для социальных сетей в основном используют Google Cloud Cloud для целей уведомления, которые вам не подходят.

Я рекомендую вам использовать диспетчер Alarm для этого. После версии KitKat (4.2) операционная система блокирует фоновый обработчик для лучшего использования аккумулятора.

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

Я не уверен в JobScheduler api для поддержки более старых версий поддержки, но так же хорош, как Alarm Manager.

Ответ 6

вы можете сделать это, используя службу, с возвратом start_sticky в "START_STICKY" сообщает ОС, чтобы воссоздать службу после того, как у нее хватило памяти, и снова вызовите onStartCommand() с нулевым намерением. START_NOT_STICKY сообщает ОС не беспокоить воссоздание службы Кроме того, есть третий код START_REDELIVER_INTENT, который сообщает ОС, чтобы воссоздать службу и повторить то же самое намерение для onStartCommand() "

и установите ТАЙМЕР с периодом 1 минута и выполните код.

Также, если вы хотите перезапустить службу, когда пользовательская сила остановит ее, вы можете сделать это "как предыдущие ответы"

  • Вы можете создать класс, который расширяет IntentService (это выполняется в фоновом потоке) в методе onDestroy этого класса, отправляет широковещательную рассылку и перезапускает службу вещания.

    @onDestroy(){
        Intent broadcastIntent = new Intent("com.example.myapp.serviceRestarted");
        sendBroadcast(broadcastIntent);
    }
    
  • Создайте класс, который расширяет широковещательный приемник

    public class RestartServiceReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            context.startService(new Intent(context, MyService.class));
        } 
    }
    
  • В своем манифесте зарегистрируйте свой сервис и получатель

    <receiver
       android:name=".RestartServiceReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="com.example.myapp.serviceRestarted" />
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    

Кроме того, вы можете использовать AlarmManager, и если вам нужно установить аварийные сигналы, которые срабатывают при Doze, используйте:

setAndAllowWhileIdle() или setExactAndAllowWhileIdle().

установите его "текущее время в секунду + 60 секунд", поэтому вы установите его в следующую минуту.

и выполните ваш код и последний, reset AlarmManager в следующую минуту.

Кроме того, вы можете запустить службу или AlarmManager после перезагрузки устройства просто используйте команду brodcastReciever, когда "RECEIVE_BOOT_COMPLETED"

и поставьте это разрешение:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

Ответ 7

Выше Lollipop, т.е. API версии 21, вы можете использовать JobScheduler для планирования JobService. Чтобы повторять задание каждую минуту, вам нужно будет планировать задание каждый раз, когда оно будет завершено, установив минимальную задержку в 60 * 1000 миллисекунд.

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService {

    boolean isWorking = false;
    boolean jobCancelled = false;

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.d("_____TAG_____", "MyJobService started!");
        isWorking = true;

        doWork(params);

        return isWorking;
    }

    private void doWork(JobParameters params) {

        if (jobCancelled)
            return;

        //Create a new thread here and do your work in it. 
        //Remember, job service runs in main thread

        Log.d("_____TAG_____", "MyJobService finished!");
        isWorking = false;
        boolean needsReschedule = false;
        jobFinished(params, needsReschedule);

        scheduleRefresh();

    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.d("_____TAG_____", "MyJobService cancelled before being completed.");
        jobCancelled = true;
        boolean needsReschedule = isWorking;
        jobFinished(params, needsReschedule);
        return needsReschedule;
    }

    private void scheduleRefresh() {

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            ComponentName componentName = new ComponentName(getApplicationContext(), MyJobService.class);
            JobInfo.Builder builder = new JobInfo.Builder(5, componentName);
            builder.setMinimumLatency(60*1000);  //1 minute
            JobInfo jobInfo = builder.build();

            JobScheduler jobScheduler = (JobScheduler)getApplicationContext().getSystemService(JOB_SCHEDULER_SERVICE);
            int resultCode = jobScheduler.schedule(jobInfo);
            if (resultCode == JobScheduler.RESULT_SUCCESS) {
                Log.d("_____TAG_____", "MyJobService scheduled!");
            } else {
                Log.d("_____TAG_____", "MyJobService not scheduled");
            }
        }
    }

}

В любое время вы можете написать общую функцию для планирования работы -

public void scheduleMyJobService() {

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        ComponentName componentName = new ComponentName(context, MyJobService.class);
        JobInfo.Builder builder = new JobInfo.Builder(5, componentName);
        builder.setMinimumLatency(60*1000);
        JobInfo jobInfo = builder.build();

        JobScheduler jobScheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
        int resultCode = jobScheduler.schedule(jobInfo);
        if (resultCode == JobScheduler.RESULT_SUCCESS) {
            Log.d("_____TAG_____", "MyJobService scheduled!");
        } else {
            Log.d("_____TAG_____", "MyJobService not scheduled");
        }
    }
}