Dispatcher de trabalho do Firebase

Este guia discute como agendar o trabalho em segundo plano usando a biblioteca Firebase Job Dispatcher do Google.

Visão geral

Uma das melhores maneiras de manter um aplicativo Android responsivo ao usuário é garantir que o trabalho complexo ou de execução prolongada seja executado em segundo plano. No entanto, é importante que o trabalho em segundo plano não afete negativamente a experiência do usuário com o dispositivo.

Por exemplo, um trabalho em segundo plano pode sondar um site a cada três ou quatro minutos para consultar alterações em um conjunto de dados específico. Isso parece benigno, no entanto, teria um impacto desastroso na vida útil da bateria. O aplicativo ativará repetidamente o dispositivo, elevará a CPU para um estado de energia mais alto, ligará os rádios, fará as solicitações de rede e processará os resultados. Isso piora porque o dispositivo não desligará imediatamente e retornará ao estado ocioso de baixa potência. O trabalho em segundo plano mal agendado pode inadvertidamente manter o dispositivo em um estado com requisitos de energia desnecessários e excessivos. Essa atividade aparentemente inocente (sondagem de um site) tornará o dispositivo inutilizável em um período relativamente curto de tempo.

O Android fornece as SEGUINTEs APIs para ajudar a executar o trabalho em segundo plano, mas por si só eles não são suficientes para o agendamento inteligente de trabalhos.

  • Serviços de Intenção – Os Serviços de Intenção são ótimos para executar o trabalho, no entanto, eles não fornecem nenhuma maneira de agendar o trabalho.
  • AlarmManager – essas APIs só permitem que o trabalho seja agendado, mas não fornecem nenhuma maneira de realmente executar o trabalho. Além disso, o AlarmManager só permite restrições baseadas em tempo, o que significa gerar um alarme em um determinado momento ou após um determinado período de tempo ter decorrido.
  • JobScheduler – o JobSchedule é uma ótima API que funciona com o sistema operacional para agendar trabalhos. No entanto, ele só está disponível para os aplicativos Android direcionados ao nível de API 21 ou superior.
  • Receptores de Difusão – um aplicativo Android pode configurar receptores de difusão para executar o trabalho em resposta a eventos ou intenções em todo o sistema. No entanto, os receptores de difusão não fornecem nenhum controle sobre quando o trabalho deve ser executado. Além disso, as alterações no sistema operacional Android restringirão quando os receptores de difusão funcionarem ou os tipos de trabalho aos quais eles podem responder.

Há dois recursos principais para executar com eficiência o trabalho em segundo plano (às vezes chamado de trabalho em segundo plano ou um trabalho):

  1. Agendamento inteligente do trabalho – é importante que, quando um aplicativo está trabalhando em segundo plano, ele o faça como um bom cidadão. O ideal é que o aplicativo não exija que um trabalho seja executado. Em vez disso, o aplicativo deve especificar condições que devem ser atendidas para quando o trabalho pode ser executado e, em seguida, agendar esse trabalho para ser executado quando as condições forem atendidas. Isso permite que o Android execute trabalho de forma inteligente. Por exemplo, as solicitações de rede podem ser executadas em lote ao mesmo tempo para fazer uso máximo da sobrecarga envolvida com a rede.
  2. Encapsulando o trabalho – o código para executar o trabalho em segundo plano deve ser encapsulado em um componente discreto que pode ser executado independentemente da interface do usuário e será relativamente fácil de reagendar se o trabalho não for concluído por algum motivo.

O Firebase Job Dispatcher é uma biblioteca do Google que fornece uma API fluente para simplificar o agendamento de trabalho em segundo plano. Ele se destina a ser o substituto do Google Cloud Manager. O Firebase Job Dispatcher consiste nas seguintes APIs:

  • Um Firebase.JobDispatcher.JobService é uma classe abstrata que deve ser estendida com a lógica que será executada no trabalho em segundo plano.
  • Um Firebase.JobDispatcher.JobTrigger declara quando o trabalho deve ser iniciado. Normalmente, isso é expresso como uma janela de tempo, por exemplo, aguarde pelo menos 30 segundos antes de iniciar o trabalho, mas execute o trabalho dentro de 5 minutos.
  • Um Firebase.JobDispatcher.RetryStrategy contém informações sobre o que deve ser feito quando um trabalho não é executado corretamente. A estratégia de repetição especifica quanto tempo aguardar antes de tentar executar o trabalho novamente.
  • Um Firebase.JobDispatcher.Constraint é um valor opcional que descreve uma condição que deve ser atendida antes que o trabalho possa ser executado, como se o dispositivo estivesse em uma rede não atendida ou carregando.
  • O Firebase.JobDispatcher.Job é uma API que unifica as APIs anteriores em para uma unidade de trabalho que pode ser agendada pelo JobDispatcher. A Job.Builder classe é usada para instanciar um Job.
  • Um Firebase.JobDispatcher.JobDispatcher usa as três APIs anteriores para agendar o trabalho com o sistema operacional e fornecer uma maneira de cancelar trabalhos, se necessário.

Para agendar o trabalho com o Firebase Job Dispatcher, um aplicativo Xamarin.Android deve encapsular o código em um tipo que estenda a JobService classe. JobService tem três métodos de ciclo de vida que podem ser chamados durante o tempo de vida do trabalho:

  • bool OnStartJob(IJobParameters parameters) – Esse método é onde o trabalho ocorrerá e sempre deve ser implementado. Ele é executado no thread main. Esse método retornará true se houver trabalho restante ou false se o trabalho for feito.
  • bool OnStopJob(IJobParameters parameters) – Isso é chamado quando o trabalho é interrompido por algum motivo. Ele deverá retornar true se o trabalho deve ser reagendado para mais tarde.
  • JobFinished(IJobParameters parameters, bool needsReschedule) – Esse método é chamado quando o JobService concluiu qualquer trabalho assíncrono.

Para agendar um trabalho, o aplicativo criará uma instância de um JobDispatcher objeto . Em seguida, um Job.Builder é usado para criar um Job objeto , que é fornecido para o JobDispatcher que tentará agendar a execução do trabalho.

Este guia discutirá como adicionar o Firebase Job Dispatcher a um aplicativo Xamarin.Android e usá-lo para agendar o trabalho em segundo plano.

Requisitos

O Firebase Job Dispatcher requer o nível 9 ou superior da API do Android. A biblioteca Firebase Job Dispatcher depende de alguns componentes fornecidos pelo Google Play Services; o dispositivo deve ter o Google Play Services instalado.

Usando a Biblioteca de Dispatcher de Trabalhos do Firebase no Xamarin.Android

Para começar a usar o Firebase Job Dispatcher, primeiro adicione o pacote NuGet Xamarin.Firebase.JobDispatcher ao projeto Xamarin.Android. Pesquise no Gerenciador de Pacotes NuGet o pacote Xamarin.Firebase.JobDispatcher (que ainda está em pré-lançamento).

Depois de adicionar a biblioteca do Firebase Job Dispatcher, crie uma JobService classe e agende-a para ser executada com uma instância do FirebaseJobDispatcher.

Criando um JobService

Todo o trabalho executado pela biblioteca do Firebase Job Dispatcher deve ser feito em um tipo que estenda a Firebase.JobDispatcher.JobService classe abstrata. A criação de um JobService é muito semelhante à criação de um Service com a estrutura do Android:

  1. Estender a JobService classe
  2. Decore a subclasse com o ServiceAttribute. Embora não seja estritamente necessário, é recomendável definir explicitamente o Name parâmetro para ajudar na depuração do JobService.
  3. Adicione um IntentFilter para declarar o JobService no AndroidManifest.xml. Isso também ajudará a biblioteca do Firebase Job Dispatcher a localizar e invocar o JobService.

O código a seguir é um exemplo do mais simples JobService para um aplicativo, usando o TPL para executar de forma assíncrona algum trabalho:

[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;
    }
}

Criando um FirebaseJobDispatcher

Antes que qualquer trabalho possa ser agendado, é necessário criar um Firebase.JobDispatcher.FirebaseJobDispatcher objeto . O FirebaseJobDispatcher é responsável por agendar um JobService. O snippet de código a seguir é uma maneira de criar uma instância do FirebaseJobDispatcher:

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

No snippet de código anterior, a GooglePlayDriver classe é que ajuda a FirebaseJobDispatcher interagir com algumas das APIs de agendamento no Google Play Services no dispositivo. O parâmetro context é qualquer Android Context, como uma Atividade. Atualmente, o GooglePlayDriver é a única IDriver implementação na biblioteca do Firebase Job Dispatcher.

A associação Xamarin.Android para o Firebase Job Dispatcher fornece um método de extensão para criar um FirebaseJobDispatcher do Context:

FirebaseJobDispatcher dispatcher = context.CreateJobDispatcher();

Depois que o FirebaseJobDispatcher tiver sido instanciado, é possível criar um Job e executar o código na JobService classe . O Job é criado por um Job.Builder objeto e será discutido na próxima seção.

Criando um Firebase.JobDispatcher.Job com o Job.Builder

A Firebase.JobDispatcher.Job classe é responsável por encapsular os metadados necessários para executar um JobService. UmJob contém informações como qualquer restrição que deve ser atendida antes que o trabalho possa ser executado, se o Job for recorrente ou quaisquer gatilhos que farão com que o trabalho seja executado. Como um mínimo, um Job deve ter uma marca (uma cadeia de caracteres exclusiva que identifica o trabalho para o FirebaseJobDispatcher) e o tipo do JobService que deve ser executado. O Firebase Job Dispatcher criará uma instância do JobService quando for a hora de executar o trabalho. Um Job é criado usando uma instância da Firebase.JobDispatcher.Job.JobBuilder classe .

O snippet de código a seguir é o exemplo mais simples de como criar um Job usando a associação Xamarin.Android:

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

O Job.Builder executará algumas verificações básicas de validação nos valores de entrada para o trabalho. Uma exceção será gerada se não for possível para o Job.Builder criar um Job. O Job.Builder criará um Job com os seguintes padrões:

  • O tempo de vida de um Job(por quanto tempo ele será agendado para ser executado) é apenas até que o dispositivo seja reinicializado , depois que o dispositivo reinicializar o Job for perdido.
  • Um Job não é recorrente – ele só será executado uma vez.
  • Um Job será agendado para ser executado assim que possível.
  • A estratégia de repetição padrão para um Job é usar uma retirada exponencial (discutida mais detalhadamente abaixo na seção Configurando uma RetryStrategy)

Agendando um trabalho

Depois de criar o Job, ele precisa ser agendado com o FirebaseJobDispatcher antes de ser executado. Há dois métodos para agendar um 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);

O valor retornado por FirebaseJobDispatcher.Schedule será um dos seguintes valores inteiros:

  • FirebaseJobDispatcher.ScheduleResultSuccess – O Job foi agendado com êxito.
  • FirebaseJobDispatcher.ScheduleResultUnknownError – Ocorreu algum problema desconhecido que impediu que o Job fosse agendado.
  • FirebaseJobDispatcher.ScheduleResultNoDriverAvailable – Um inválido IDriver foi usado ou o IDriver estava de alguma forma indisponível.
  • FirebaseJobDispatcher.ScheduleResultUnsupportedTrigger – Não Trigger havia suporte para o .
  • FirebaseJobDispatcher.ScheduleResultBadService – O serviço não está configurado corretamente ou não está disponível.

Configurando um trabalho

É possível personalizar um trabalho. Exemplos de como um trabalho pode ser personalizado incluem o seguinte:

Cada um desses tópicos será mais discutido nas seções a seguir.

Passando parâmetros para um trabalho

Os parâmetros são passados para um trabalho criando um Bundle que é passado junto com o Job.Builder.SetExtras método :

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

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

O Bundle é acessado da IJobParameters.Extras propriedade no OnStartJob método :

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

Configuração de restrições

As restrições podem ajudar a reduzir os custos ou o esvaziamento de bateria no dispositivo. A Firebase.JobDispatcher.Constraint classe define essas restrições como valores inteiros:

  • Constraint.OnUnmeteredNetwork – Execute o trabalho somente quando o dispositivo estiver conectado a uma rede não atendida. Isso é útil para impedir que o usuário incorre em encargos de dados.
  • Constraint.OnAnyNetwork – Execute o trabalho em qualquer rede à qual o dispositivo esteja conectado. Se especificado junto com Constraint.OnUnmeteredNetwork, esse valor terá prioridade.
  • Constraint.DeviceCharging – Execute o trabalho somente quando o dispositivo estiver carregando.

As restrições são definidas com o Job.Builder.SetConstraint método :

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

O JobTrigger fornece diretrizes para o sistema operacional sobre quando o trabalho deve ser iniciado. Um JobTrigger tem uma janela de execução que define um horário agendado para quando o Job deve ser executado. A janela de execução tem um valor de janela inicial e um valor de janela final . A janela inicial é o número de segundos que o dispositivo deve aguardar antes de executar o trabalho e o valor da janela final é o número máximo de segundos a aguardar antes de executar o Job.

Um JobTrigger pode ser criado com o Firebase.Jobdispatcher.Trigger.ExecutionWindow método . Por exemplo Trigger.ExecutionWindow(15,60) , significa que o trabalho deve ser executado entre 15 e 60 segundos a partir de quando ele está agendado. O Job.Builder.SetTrigger método é usado para

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

O padrão JobTrigger para um trabalho é representado pelo valor Trigger.Now, que especifica que um trabalho seja executado o mais rápido possível após o agendamento.

Configurando uma RetryStrategy

O Firebase.JobDispatcher.RetryStrategy é usado para especificar quanto atraso um dispositivo deve usar antes de tentar executar novamente um trabalho com falha. Um RetryStrategy tem uma política, que define qual algoritmo de base de tempo será usado para agendar novamente o trabalho com falha e uma janela de execução que especifica uma janela na qual o trabalho deve ser agendado. Essa janela de reagendamento é definida por dois valores. O primeiro valor é o número de segundos a aguardar antes de reagendar o trabalho (o valor inicial de retirada ) e o segundo número é o número máximo de segundos antes que o trabalho precise ser executado (o valor máximo de retirada ).

Os dois tipos de políticas de repetição são identificados por esses valores int:

  • RetryStrategy.RetryPolicyExponential – Uma política de retirada exponencial aumentará o valor de retirada inicial exponencialmente após cada falha. Na primeira vez que um trabalho falhar, a biblioteca aguardará o intervalo de _initial especificado antes de reagendar o trabalho – exemplo, 30 segundos. Na segunda vez que o trabalho falhar, a biblioteca aguardará pelo menos 60 segundos antes de tentar executar o trabalho. Após a terceira tentativa com falha, a biblioteca aguardará 120 segundos e assim por diante. O padrão RetryStrategy para a biblioteca do Firebase Job Dispatcher é representado pelo RetryStrategy.DefaultExponential objeto . Ele tem uma retirada inicial de 30 segundos e uma retirada máxima de 3600 segundos.
  • RetryStrategy.RetryPolicyLinear – Essa estratégia é uma retirada linear que o trabalho deve ser reagendado para ser executado em intervalos definidos (até que seja bem-sucedido). A retirada linear é mais adequada para o trabalho que deve ser concluído o mais rápido possível ou para problemas que rapidamente se resolve. A biblioteca do Firebase Job Dispatcher define um RetryStrategy.DefaultLinear que tem uma janela de reagendamento de pelo menos 30 segundos e até 3600 segundos.

É possível definir um personalizado RetryStrategy com o FirebaseJobDispatcher.NewRetryStrategy método . Ele usa três parâmetros:

  1. int policy – A política é um dos valores anteriores RetryStrategy , RetryStrategy.RetryPolicyLinearou RetryStrategy.RetryPolicyExponential.
  2. int initialBackoffSeconds – A retirada inicial é um atraso, em segundos, que é necessário antes de tentar executar o trabalho novamente. O valor padrão para isso é 30 segundos.
  3. int maximumBackoffSeconds – O valor máximo de retirada declara o número máximo de segundos para atrasar antes de tentar executar o trabalho novamente. O valor padrão é 3600 segundos.
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();

Cancelando um trabalho

É possível cancelar todos os trabalhos que foram agendados ou apenas um único trabalho usando o FirebaseJobDispatcher.CancelAll() método ou o FirebaseJobDispatcher.Cancel(string) método :

int cancelResult = dispatcher.CancelAll(); 

// to cancel a single job:

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

Qualquer um dos métodos retornará um valor inteiro:

  • FirebaseJobDispatcher.CancelResultSuccess – O trabalho foi cancelado com êxito.
  • FirebaseJobDispatcher.CancelResultUnknownError – Um erro impediu que o trabalho fosse cancelado.
  • FirebaseJobDispatcher.CancelResult.NoDriverAvailable – O FirebaseJobDispatcher não pode cancelar o trabalho, pois não há nenhum válido IDriver disponível.

Resumo

Este guia discutiu como usar o Firebase Job Dispatcher para executar o trabalho de forma inteligente em segundo plano. Ele discutiu como encapsular o trabalho a ser executado como um JobService e como usar o FirebaseJobDispatcher para agendar esse trabalho, especificando os critérios com um JobTrigger e como as falhas devem ser tratadas com um RetryStrategy.