Диспетчер заданий Firebase

В этом руководстве описано, как планировать фоновую работу с помощью библиотеки диспетчера заданий Firebase от Google.

Обзор

Один из лучших способов поддерживать хорошую отзывчивость приложения Android — выполнять сложную или длительную работу в фоновом режиме. При этом важно, чтобы фоновая работа не влияла негативно на взаимодействие пользователя с устройством.

Например, фоновое задание может опрашивать веб-сайт каждые три или четыре минуты, чтобы получать изменения в определенном наборе данных. Это кажется пустяком, но это может повлиять на время работы аккумулятора. Приложение будет раз за разом выводить устройство из спящего режима, повышать уровень энергопотребления ЦП, включать беспроводную связь, выполнять сетевые запросы и обрабатывать результаты. Все усложняется тем, что устройство не будет немедленно выключаться и возвращаться в режим ожидания с низким энергопотреблением. Плохо спланированная фоновая работа может привести к тому, что устройство самопроизвольно и без необходимости будет переходить в режим повышенного энергопотребления. На первый взгляд безобидное действие (опрос веб-сайта) довольно быстро сделает устройство непригодным для использования.

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

  • Службы намерений — службы намерений отлично подходят для выполнения работы, однако они не предоставляют никаких способов планирования работы.
  • AlarmManager — эти API позволяют планировать работу только, но не предоставляют возможности фактически выполнять работу. Кроме того, AlarmManager поддерживает только ограничения на основе времени, то есть создает оповещение в определенное время или по истечении определенного времени.
  • JobScheduler — JobSchedule — отличный API, который работает с операционной системой для планирования заданий. Однако он доступен только для тех приложений Android, которые нацелены на уровень API 21 или более поздней версии.
  • Широковещательные приемники — приложение Android может настроить широковещательные приемники для выполнения работы в ответ на системные события или намерения. При этом широковещательные приемники не позволяют управлять временем выполнения заданий. Кроме того, изменения в операционной системе Android в будущем ограничат возможность выполнять широковещательные приемники, а также типы работы, на которую они могут реагировать.

Существуют два ключевых аспекта эффективного выполнения фоновой работы (иногда называемой фоновым заданием или просто заданием):

  1. Интеллектуальное планирование работы — важно, чтобы, когда приложение выполняет работу в фоновом режиме, что делает это как хороший гражданин. В идеале приложение не должно требовать выполнения задания. Вместо этого оно должно указать условия, при которых задание может быть выполнено, а затем запланировать выполнение работы при выполнении этих условий. Это позволяет Android выполнять работу интеллектуально. Например, сетевые запросы могут собираться в пакет и выполняться одновременно, чтобы оптимизировать затраты на работу с сетью.
  2. Инкапсулируя работу. Код для выполнения фоновой работы должен быть инкапсулирован в дискретном компоненте, который может выполняться независимо от пользовательского интерфейса и будет относительно легко перепланировать работу, если работа не завершится по какой-то причине.

Диспетчер заданий Firebase — это библиотека от Google, которая предоставляет текучий API для упрощения планирования фоновой работы. Она должна заменить Google Cloud Manager. Диспетчер заданий Firebase содержит следующие интерфейсы API.

  • Firebase.JobDispatcher.JobService — это абстрактный класс, который должен быть расширен с помощью логики, которая будет выполняться в фоновом задании.
  • Firebase.JobDispatcher.JobTrigger объявляет, когда задание должно быть запущено. Обычно это период времени, например, ожидание не менее 30 секунд перед запуском задания, но при этом задание должно быть запущено в течение 5 минут.
  • Firebase.JobDispatcher.RetryStrategy содержит сведения о том, что следует делать, если задание не выполняется должным образом. Стратегия повтора указывает время ожидания перед повторной попыткой выполнения задания.
  • Firebase.JobDispatcher.Constraint — это необязательное значение, описывающее условие, которое должно быть выполнено для запуска задания. Например, устройство не должно находиться в сети с оплатой трафика или заряжаться.
  • Firebase.JobDispatcher.Job — это API, который объединяет предыдущие API в единицу работы, которую может запланировать JobDispatcher. Класс Job.Builder используется для создания экземпляра Job.
  • Firebase.JobDispatcher.JobDispatcher использует три описанных выше API, чтобы планировать работу с помощью операционной системой и предоставить способ отмены заданий, если это потребуется.

Чтобы запланировать работу с помощью диспетчера заданий Firebase, приложение Xamarin.Android должно инкапсулировать код в типе, который расширяет класс JobService. JobService содержит три метода жизненного цикла, которые могут быть вызваны в течение времени существования задания.

  • bool OnStartJob(IJobParameters parameters) — Этот метод заключается в том, где будут выполняться работы и всегда должны быть реализованы. Он выполняется в основном потоке. Этот метод возвращает true, если работа еще не выполнена, и false, если она завершена.
  • bool OnStopJob(IJobParameters parameters) — Это вызывается, когда задание останавливается по какой-то причине. Он должен возвращать true, если задание следует повторно запланировать.
  • JobFinished(IJobParameters parameters, bool needsReschedule) — Этот метод вызывается при JobService завершении любой асинхронной работы.

Чтобы запланировать задание, приложение создаст экземпляр объекта JobDispatcher. Затем Job.Builder используется для создания объекта Job. Этот объект предоставляется JobDispatcher, который попытается запланировать выполнение задания.

В этом руководство описано, как добавить диспетчер заданий Firebase в приложение Xamarin.Android и использовать его для планирования фоновой работы.

Требования

Диспетчеру заданий Firebase требуется уровень API Android 9 или более поздней версии. Библиотека диспетчера заданий Firebase использует некоторые компоненты, предоставляемые Сервисами Google Play, поэтому устройстве должны быть установлены Сервисы Google Play.

Использование библиотеки диспетчера заданий Firebase в Xamarin.Android

Чтобы приступить к работе с диспетчером заданий Firebase, сначала добавьте пакет NuGet Xamarin.Firebase.JobDispatcher в проект Xamarin.Android. Найдите в диспетчере пакетов NuGet пакет Xamarin.Firebase.JobDispatcher (который пока находится на этапе предварительной версии).

После добавления библиотеки диспетчера заданий Firebase создайте класс JobService и запланируйте его запуск с помощью экземпляра FirebaseJobDispatcher.

Создание JobService

Вся работа, выполняемая библиотекой диспетчера заданий Firebase, должна быть выполнена в типе, расширяющем абстрактный класс Firebase.JobDispatcher.JobService. Создание JobService очень похоже на создание Service с помощью платформы Android.

  1. Расширьте класс JobService.
  2. Декорируйте подкласс с помощью ServiceAttribute. Хотя это и не обязательно, рекомендуется явно задать параметр Name, чтобы упростить отладку JobService.
  3. Добавьте IntentFilter, чтобы объявить JobService в AndroidManifest.xml. Это также поможет библиотеке диспетчера заданий Firebase найти и вызвать JobService.

Следующий код является примером простейшего JobService для приложения, использующего TPL для асинхронного выполнения некоторой работы.

[Service(Name = "com.xamarin.fjdtestapp.DemoJob")]
[IntentFilter(new[] {FirebaseJobServiceIntent.Action})]
public class DemoJob : JobService
{
    static readonly string TAG = "X:DemoService";

    public override bool OnStartJob(IJobParameters jobParameters)
    {
        Task.Run(() =>
        {
            // Work is happening asynchronously (code omitted)
                       
        });

        // Return true because of the asynchronous work
        return true;  
    }

    public override bool OnStopJob(IJobParameters jobParameters)
    {
        Log.Debug(TAG, "DemoJob::OnStartJob");
        // nothing to do.
        return false;
    }
}

Создание FirebaseJobDispatcher

Прежде чем можно будет запланировать работу, необходимо создать объект Firebase.JobDispatcher.FirebaseJobDispatcher. FirebaseJobDispatcher отвечает за планирование JobService. В следующем фрагменте кода показан один из способов создания экземпляра FirebaseJobDispatcher.

// This is the "Java" way to create a FirebaseJobDispatcher object
IDriver driver = new GooglePlayDriver(context);
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(driver);

В предыдущем фрагменте кода GooglePlayDriver — это класс, помогающий FirebaseJobDispatcher взаимодействовать с некоторыми API планирования в Сервисах Google Play на устройстве. Параметр context — это любой Context Android, например действие. Сейчас GooglePlayDriver является единственной реализацией IDriver в библиотеке диспетчера заданий Firebase.

Привязка Xamarin.Android для диспетчера заданий Firebase обеспечивает метод расширения для создания FirebaseJobDispatcher из Context.

FirebaseJobDispatcher dispatcher = context.CreateJobDispatcher();

После создания экземпляра FirebaseJobDispatcher можно создать Job и выполнить код в классе JobService. Job создается объектом Job.Builder и будет рассмотрен в следующем разделе.

Создание Firebase.JobDispatcher.Job с помощью Job.Builder

Класс Firebase.JobDispatcher.Job отвечает за инкапсуляцию метаданных, необходимых для запуска JobService. Job содержит такие сведения, как любые ограничения, которые должны быть соблюдены для выполнения задания, если Job повторяется или для выполнения задания используются триггеры. Как минимум, Job должен содержать тег (уникальная строка, указывающая задание для FirebaseJobDispatcher) и тип выполняемого объекта JobService. Диспетчер заданий Firebase создаст экземпляр JobService, когда наступит время запуска задания. Job создается с помощью экземпляра класса Firebase.JobDispatcher.Job.JobBuilder.

Следующий фрагмент кода является простым примером создания Job с помощью привязки Xamarin.Android.

Job myJob = dispatcher.NewJobBuilder()
                      .SetService<DemoJob>("demo-job-tag")
                      .Build();

Job.Builder выполнит несколько простых проверок входных значений для задания. Если Job.Builder не удастся создать Job, будет порождено исключение. Job.Builder создаст Job со следующими параметрами по умолчанию.

  • Время Jobсуществования (как долго оно будет запланировано на выполнение) только до перезагрузки устройства — после перезагрузки Job устройства устройство теряется.
  • Не Job повторяется — он будет выполняться только один раз.
  • Запуск Job будет запланирован как можно скорее.
  • Стратегия повтора по умолчанию для Job заключается в использовании экспоненциальной задержки (см. дополнительные сведения ниже в разделе Настройка RetryStrategy).

Планирование задания

После создания Job его необходимо запланировать с помощью FirebaseJobDispatcher, прежде чем выполнять. Запланировать Job можно двумя способами.

// This will throw an exception if there was a problem scheduling the job
dispatcher.MustSchedule(myJob);

// This method will not throw an exception; an integer result value is returned
int scheduleResult = dispatcher.Schedule(myJob);

Значение, возвращаемое FirebaseJobDispatcher.Schedule, будет одним из следующих целочисленных значений:

  • FirebaseJobDispatcher.ScheduleResultSuccessJob— Успешно запланировано.
  • FirebaseJobDispatcher.ScheduleResultUnknownError — Возникла неизвестная проблема, из-за которой не Job было запланировано.
  • FirebaseJobDispatcher.ScheduleResultNoDriverAvailable — Недопустимый IDriver использовался или IDriver каким-то образом недоступен.
  • FirebaseJobDispatcher.ScheduleResultUnsupportedTrigger — Не Trigger поддерживается.
  • FirebaseJobDispatcher.ScheduleResultBadService — служба настроена неправильно или недоступна.

Настройка задания

Задание можно настроить. Ниже приведены примеры того, как можно настроить задание.

В следующих разделах каждая из этих тем рассматривается более подробно.

Передача параметров в задание

Параметры передаются в задание путем создания Bundle, который передается вместе с методом Job.Builder.SetExtras.

Bundle jobParameters = new Bundle();
jobParameters.PutInt(FibonacciCalculatorJob.FibonacciPositionKey, 25);

Job myJob = dispatcher.NewJobBuilder()
                      .SetService<DemoJob>("demo-job-tag")
                      .SetExtras(jobParameters)
                      .Build();

Доступ к Bundle осуществляется из свойства IJobParameters.Extras метода OnStartJob.

public override bool OnStartJob(IJobParameters jobParameters)
{
    int position = jobParameters.Extras.GetInt(FibonacciPositionKey, DEFAULT_VALUE);
    
    // rest of code omitted
} 

Установка ограничений

Ограничения могут снизить расходы или истощение аккумулятора на устройстве. Класс Firebase.JobDispatcher.Constraint определяет эти ограничения как целочисленные значения.

  • Constraint.OnUnmeteredNetwork — только запустите задание, если устройство подключено к неметриченной сети. Это удобно, чтобы пользователи не переплачивали за передачу данных.
  • Constraint.OnAnyNetwork — Запустите задание в любой сети, к которому подключено устройство. Если указано вместе с Constraint.OnUnmeteredNetwork, то это значение будет иметь приоритет.
  • Constraint.DeviceCharging — Запустите задание только в том случае, если устройство зарядится.

Ограничения задаются с помощью метода Job.Builder.SetConstraint.

Job myJob = dispatcher.NewJobBuilder()
                      .SetService<DemoJob>("demo-job-tag")
                      .SetConstraint(Constraint.DeviceCharging)
                      .Build();

JobTrigger предоставляет инструкции операционной системе о том, когда должно начаться выполнение задания. У JobTrigger есть период выполнения, который определяет запланированное время выполнения Job. У периода выполнения имеются значения периода начала и периода конца. Период начала — это количество секунд, которое устройство должно ожидать перед выполнением задания, а период конца — это максимальное число секунд ожидания перед запуском Job.

JobTrigger можно создать с помощью метода Firebase.Jobdispatcher.Trigger.ExecutionWindow. Например, Trigger.ExecutionWindow(15,60) означает, что задание должно быть запущено через 15–60 секунд после времени, на которое оно запланировано. Используется метод Job.Builder.SetTrigger.

JobTrigger myTrigger = Trigger.ExecutionWindow(15,60);
Job myJob = dispatcher.NewJobBuilder()
                      .SetService<DemoJob>("demo-job-tag")
                      .SetTrigger(myTrigger)
                      .Build();

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

Настройка RetryStrategy

Firebase.JobDispatcher.RetryStrategy используется для указания того, какую задержку должно использовать устройство, прежде чем пытаться повторно запустить невыполненное задание. У RetryStrategy имеется политика, которая определяет временной алгоритм, который будет использоваться для повторного планирования невыполненного задания, и период выполнения, на который это задание должно быть запланировано. Данные период повторного планирования определяется двумя значениями. Первое значение — это время ожидания в секундах перед повторным планированием задания (значение начальной задержки). Второе число — это максимальное время в секундах, по истечении которого задание должно быть запущено (значение максимальной задержки).

Два типа политик повтора определяют приведенные ниже целочисленные значения.

  • RetryStrategy.RetryPolicyExponential— Экспоненциальная политика обратной передачи увеличит начальное значение обратной передачи экспоненциально после каждого сбоя. При первом сбое задания библиотека ожидает _initial интервала, указанного перед перепланированием задания, например 30 секунд. При второй неудаче библиотека будет ожидать не менее 60 секунд перед попыткой повторного запуска. После третьей неудачной попытки библиотека будет ждать 120 секунд и т. д. RetryStrategy по умолчанию для библиотеки диспетчера заданий Firebase представляет объект RetryStrategy.DefaultExponential. Он использует начальную задержку в 30 секунд и максимальную задержку в 3600 секунд.
  • RetryStrategy.RetryPolicyLinear — Эта стратегия представляет собой линейную обратную передачу , которую задание должно быть перепланировано для выполнения с заданными интервалами (до тех пор, пока задание не будет выполнено успешно). Линейная задержка лучше всего подходит для работы, которая должна быть выполнена как можно скорее, или при наличии проблем, которые быстро исчезают сами по себе. Библиотека диспетчера заданий Firebase определяет RetryStrategy.DefaultLinear с периодом повторного планирования от 30 до 3600 секунд.

Можно определить пользовательскую стратегию RetryStrategy с помощью метода FirebaseJobDispatcher.NewRetryStrategy. Он принимает три параметра.

  1. int policy— политика является одним из предыдущих RetryStrategy значений или RetryStrategy.RetryPolicyLinearRetryStrategy.RetryPolicyExponential.
  2. int initialBackoffSeconds — начальная обратная передача — это задержка в секундах, которая требуется перед попыткой повторного выполнения задания. По умолчанию это значение равно 30 секундам.
  3. int maximumBackoffSeconds — максимальное значение отката объявляет максимальное количество секунд, которое необходимо отложить перед попыткой повторного выполнения задания. Значение по умолчанию — 3600 секунд.
RetryStrategy retry = dispatcher.NewRetryStrategy(RetryStrategy.RetryPolicyLinear, initialBackoffSeconds, maximumBackoffSet);

// Create a Job and set the RetryStrategy via the Job.Builder
Job myJob = dispatcher.NewJobBuilder()
                      .SetService<DemoJob>("demo-job-tag")
                      .SetRetryStrategy(retry)
                      .Build();

Отмена задания

Можно отменить все запланированные задания или только одно задание, используя метод FirebaseJobDispatcher.CancelAll() или метод FirebaseJobDispatcher.Cancel(string).

int cancelResult = dispatcher.CancelAll(); 

// to cancel a single job:

int cancelResult = dispatcher.Cancel("unique-tag-for-job");

Любой из этих методов возвращает целочисленное значение.

  • FirebaseJobDispatcher.CancelResultSuccess — Задание было успешно отменено.
  • FirebaseJobDispatcher.CancelResultUnknownError — Ошибка предотвратила отмену задания.
  • FirebaseJobDispatcher.CancelResult.NoDriverAvailable — Не FirebaseJobDispatcher удается отменить задание, так как не существует допустимого IDriver значения.

Итоги

В этом руководство описано, как использовать диспетчер заданий Firebase для интеллектуального выполнения работы в фоновом режиме. Мы изучили, как инкапсулировать в JobService работу, которую необходимо выполнить, и как с помощью FirebaseJobDispatcher запланировать эту работу, указывая критерии в JobTrigger и способ обработки ошибок в RetryStrategy.