Android

10 jul, 2018

WorkManager – O que é? Como usar?

Publicidade

O que é o WorkManager?

No Google IO 2018 foi liberado o Android Jetpack, um conjunto de bibliotecas, ferramentas e orientações arquiteturais para ajudar, tornar rápido e facilitar a criação de aplicativos para Android.

O WorkManger está incluído no Jetpack, mas o que é o WorkManager? É uma API que facilita a especificação de tarefas assíncronas, levando em consideração a periodicidade e as condições em que as tarefas serão executadas. Usando um conjunto de APIs, que falarei abaixo, podemos criar uma tarefa e entregá-la ao WorkManager para execução imediata ou no momento apropriado.

Lista de tarefas pré-feriado

Por exemplo, imagine que nosso aplicativo tenha que fazer o download de um podcast semanalmente. Usando essas classes, você pode configurar esta tarefa, escolher as circunstâncias apropriadas para execução (como “somente enquanto o dispositivo estiver conectado à internet”) e entregá-la ao WorkManager para ser executada quando as condições forem atendidas. Sua tarefa está garantida para ser executada, mesmo se o aplicativo for forçado a sair ou o dispositivo for reinicializado.

O WorkManager consegue escolher qual a melhor forma de executar as tarefas baseadas no estado do aplicativo e no nível da API utilizada. Se ele executar uma tarefa enquanto o aplicativo estiver em execução, poderá fazer isto em uma nova thread no processo do seu aplicativo. Se o aplicativo não estiver em execução, ele vai escolher uma maneira apropriada de agendar a tarefa em segundo plano, levando em consideração o nível da API do dispositivo e das dependências incluídas; neste caso, ele poderá usar JobScheduler, Firebase Job Dispatcher ou AlarmManager.

Agora, vamos ver um pouco do que podemos fazer!

Vamos lá usar o WorkManager!

Como usar o WorkManager?

Vamos lá, primeiramente devemos adicionar o WorkManager em nosso aplicativo. Para isto, devemos adicionar no arquivo “build.gradle” da aplicação as seguintes linhas abaixo:

dependencies {
    def work_version = "1.0.0-alpha01"
    implementation "android.arch.work:work-runtime:$work_version"
    implementation "android.arch.work:work-firebase:$work_version"
}

O nosso segundo passo é criar o nosso Worker e implementar o método doWork, onde faremos a tarefa. O método doWork retorna um WorkerResult e podemos ter três retornos:

  • FAILURE: em caso de falha ou de não conformidades.
  • RETRY: significa que houve um erro transitório, ou seja, se o dispositivo perdeu a conexão de rede no meio, então teremos que tentar novamente depois de algum tempo.
  • SUCCESS: quando a execução for completada com sucesso.

Logo abaixo, temos um exemplo no qual usamos uma url passada como parâmetro e requisitamos um download ao DownloadManager:

public class DownloadWorker extends Worker {

    @NonNull
    @Override
    public WorkerResult doWork() {
        String url = getInputData().getString("key_url", "");


        if (URLUtil.isValidUrl(url)) {
            long downloadId = downloadFile(url);
            Data outputData = new Data.Builder()
                .putLong("key_download_id", downloadId).build();
            setOutputData(outputData);
            return WorkerResult.SUCCESS;
        } else {
            return WorkerResult.FAILURE;
        }

    }

    public long downloadFile(String url) {
        Long result = -1L;

        DownloadManager downloadManager = (DownloadManager)
                getApplicationContext()
                    .getSystemService(Context.DOWNLOAD_SERVICE);

        if (downloadManager != null) {
            DownloadManager.Request request = 
                new DownloadManager.Request(Uri.parse(url));
            result = downloadManager.enqueue(request);
        }

        return result;
    }
}

Como podemos ver acima, podemos adicionar dados ao retorno do método doWork através do método setOutputData passando um Data como parâmetro.

Por fim, vamos agendar a nossa tarefa, mas antes, deixa eu te mostrar alguns Componentes importantes para fazer isso. Primeiro, temos dois tipos de WorkRequest implementados: OneTimeWorkRequest e PeriodicWorkRequest.

  • OneTimeWorkRequest: uma classe que representa uma solicitação para trabalho não repetitivo. Exemplo: fazer o upload de uma foto ao clicar no botão.
  • PeriodicWorkRequest: uma classe que representa uma solicitação para trabalhos que tem uma repetição. Exemplo: baixar o podcast toda semana sem que o usuário use o aplicativo.

Logo abaixo temos um exemplo no qual estamos agendando um download ao entrar na activity:

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    Data inputData = new Data.Builder()
                        .putString("key_url", 
                                   "http://www.awitness.org/prophecy.zip")
                        .build();
    Constraints constraints = new Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build();

    OneTimeWorkRequest otwRequest =
                new OneTimeWorkRequest.Builder(DownloadWorker.class)
                        .setInputData(inputData)
                        .setConstraints(constraints).build();
    WorkManager.getInstance().enqueue(otwRequest);
}

E uma das coisas mais legais é que você pode adicionar Constraints para que a sua task seja executada apenas quando satisfazer essas condições. No exemplo acima, o DownloadWorker será executado apenas quando o device estiver conectado à internet.

Outra coisa legal, é que podemos ficar observando a mudança de estados da nossa tarefa e tomar alguma decisão baseado neles:

LiveData<WorkStatus> status = 
    WorkManager.getInstance().getStatusById(otwRequest.getId());
status.observe(this, new Observer<WorkStatus>() {
    @Override
    public void onChanged(@Nullable WorkStatus workStatus) {
        if (workStatus != null && workStatus.getState().isFinished()) {
           //TODO: Faca algo;
        }
    }
});

Gostaria de agradecer ao Rodrigo Perazzo por me encorajar a escrever e revisar meu artigo!