Entendendo o Ciclo de Vida do Work Manager no Android: Estratégias para Lidar com Falhas e Evitar Loops Exponenciais
Tags: android, kotlin
Published: 2024-03-30 00:50:12

## Introdução

O WorkManager é uma ferramenta essencial para lidar com trabalhos persistentes em aplicativos Android. Trabalhos persistentes são aqueles que permanecem agendados mesmo após reinicializações do aplicativo ou do sistema operacional. Devido à sua capacidade de executar tarefas em segundo plano de forma confiável e eficiente, o WorkManager tornou-se a principal API recomendada para essa finalidade.

Ao usar o WorkManager, garantimos que nossos serviços sejam executados conforme agendado, proporcionando uma experiência consistente para os usuários, mesmo em condições adversas, como reinicializações do dispositivo. Esta ferramenta é fundamental para garantir a execução adequada de tarefas em segundo plano, como sincronização de dados, notificações e outras operações que não exigem interação imediata do usuário.

Neste artigo, vamos explorar a proposta é explorar o ciclo de vida do WorkManager em cenários de falha e discutir os perigos de uma abordagem de retentativa sem controle, destacando a importância de estratégias adequadas para lidar com falhas e evitar loops exponenciais nos workers do WorkManager.

## Ciclo de Vida 

![Clico de vida Work Manager](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u6epafk0z2xvklm0ro0u.png)

Ao agendar um trabalho no WorkManager, acontecem algumas etapas essenciais:

1. Geração de ID Único: O WorkManager gera um ID único para identificar aquele trabalho especificamente.

1. Definição do Tempo de Execução: É especificado o tempo em que o trabalho deve ser executado, seja imediatamente, em um horário específico ou periodicamente.

No seguinte cenário hipotético, imaginemos que agendamos um trabalho adiável para ser executado a cada hora, com a finalidade de enviar dados para um servidor. Caso ocorra uma falha durante esse processo, nossa intenção é implementar uma retentativa automática.

Aqui está o código de exemplo do nosso Worker em Kotlin

```kotlin
class SampleWorker(context: Context, parameters: WorkerParameters) : CoroutineWorker(context, parameters) {

    override suspend fun doWork(): Result {
        Log.d("SampleWorker", "Iniciando o worker")
        val random = Random.nextInt(0, 10)
        delay(500) // Simula uma operação demorada
        Log.d("SampleWorker", "Worker $random ...")
        return if (random % 2 == 0) {
            Log.d("SampleWorker", "Retentativa do worker ...")
            Result.retry() // Retorna a instrução de retentativa
        } else {
            Log.d("SampleWorker", "Falha do worker ...")
            Result.failure() // Retorna a instrução de falha
        }
    }
}

```

Este Worker simula uma conexão com um serviço externo. No exemplo fornecido, sempre que o número gerado aleatoriamente for par, o Worker entrará no fluxo de retentativa, conforme especificado pela instrução Result.retry().

Esse código ilustra um cenário comum de implementação de retentativa automática em um Worker do WorkManager, crucial para lidar com falhas temporárias em operações de rede ou servidor. 

Vamos agendar esse Worker as configurações que vou usar é para fim de teste

```kotlin
val request = PeriodicWorkRequestBuilder<SampleWorker>(15, TimeUnit.MINUTES)
            .addTag("WorkerLifecycleManager")
            .addTag("OneTimeWorkerLifecycleManager")
            .setBackoffCriteria(BackoffPolicy.LINEAR, backoffDelay = 5L , TimeUnit.MINUTES)
            .build()
        
        WorkManager.getInstance(context).enqueueUniquePeriodicWork("SampleWorker", ExistingPeriodicWorkPolicy.KEEP, request)
        
```

estamos agendando o trabalhador para funcionar a cada 15 minutos e em caso de falha rodar em 5 minutos. Essa estratégia garante que o envio de informações seja resiliente assegurando a integridade e confiabilidade na comunicação com o servidor mas tem um problema oculto nessa estrategia podendo criar fluxo de trabalho exponencial.

Você pode estar se perguntando como isso pode ocorrer, especialmente se você definiu no agendamento que se já houver um worker agendado, ele deve ser ignorado:

```kotlin
WorkManager.getInstance(context).enqueueUniquePeriodicWork("SampleWorker", ExistingPeriodicWorkPolicy.KEEP, request)
```

No entanto, é crucial esclarecer que essa configuração se aplica exclusivamente a novos agendamentos e não afeta os trabalhos já em execução. É fundamental compreender que cada Worker opera de maneira independente, inclusive os periódicos, persistindo até a conclusão de suas tarefas. Portanto, caso um trabalho anterior não tenha sido concluído no momento da próxima execução agendada, isso resultará em dois Workers funcionando simultaneamente.

Considerando um cenário em que o servidor fica fora do ar por 4 horas, isso significa que em um único dispositivo teremos até 16 Workers rodando ao mesmo tempo. Se mil dispositivos estiverem usando seu aplicativo, isso resultará em 16000 requisições que seu servidor terá que lidar assim que voltar online. Dependendo das circunstâncias, essa sobrecarga pode levar a um cenário semelhante a um ataque DDoS em seu próprio servidor.

Podendo ser representado na função matemática: 

f(x) = {x/4}

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/30csv2ux6jg9602z4j5u.png)

Aonde:
X = representa o número de intervalos de 15 minutos.
4 = é a quantidade de execução que teriámos em menos de 1 hora

### Conclusão

Explorar o WorkManager e suas estratégias de execução foi uma verdadeira montanha-russa! Aprendi que o segredo para lidar com falhas e evitar loops malucos está em definir limites inteligentes para as retentativas. Usar a propriedade runAttemptCount do Worker é como ter um contador de tentativas embutido, o que nos ajuda a tomar decisões mais espertas e evitar problemas.

Uma estratégia legal que descobri foi dar um tempo entre as retentativas, tipo um respiro para o sistema se acalmar antes de tentar novamente. Isso evita sobrecargas repentinas e dá uma chance melhor para o trabalho ser concluído com sucesso.

Aprendi também que cada Worker é como um super-herói único, e devemos respeitar sua vez na fila. Se todos começarem a agir ao mesmo tempo, é como um caos total no sistema!

No geral, foi uma jornada interessante com altos e baixos, mas o WorkManager definitivamente adicionou um poder extra aos nossos aplicativos. A lição mais importante que tirei disso tudo? "Com grandes poderes, vêm grandes responsabilidades" - e isso é especialmente verdadeiro quando se trata de trabalhos em segundo plano no Android!