A maioria dos aplicativos do mundo real precisa executar tarefas em segundo plano de longa duração. Por
exemplo, um aplicativo pode carregar arquivos para um servidor, sincronizar dados de um servidor e salvá-los em
um banco de dados Room
, enviar registros para um servidor ou executar operações caras em dados.
Essas operações devem ser realizadas em segundo plano, fora da thread de IU (thread principal). As tarefas em
segundo plano consomem os recursos limitados de um dispositivo, como RAM e bateria. Isso pode resultar em uma
experiência ruim para o usuário se não for manuseado corretamente.
Neste tutorial, você aprende como usar o WorkManager
para agendar uma tarefa em segundo plano de forma otimizada e
eficiente. Para saber mais sobre outras soluções disponíveis para processamento em segundo plano no Android,
consulte o Guia para
processamento em
segundo plano.
ViewModel
, LiveData
e Room
Componentes de arquitetura do Android.LiveData
.Worker
, que representa uma unidade de trabalho.WorkRequest
para solicitar o trabalho a ser executado.WorkRequest
para definir como e quando um
trabalhador deve
ser executado.
WorkManager
para agendar tarefas em segundo plano.WorkRequest
.WorkRequest
periódico que é executado uma vez por dia.Neste tutorial, você trabalha no aplicativo DevBytes que desenvolveu em um tutorial anterior. (Se você não tem este aplicativo, pode baixar o código inicial para esta lição).
O aplicativo DevBytes exibe uma lista de vídeos DevByte, que são tutoriais curtos feitos pela equipe de relações de desenvolvedor do Google Android. Os vídeos apresentam os recursos do desenvolvedor e as práticas recomendadas para o desenvolvimento Android.
Você aprimora a experiência do usuário no aplicativo obtendo previamente os vídeos uma vez por dia. Isso garante que o usuário obtenha novos conteúdos assim que abrir o aplicativo.
Nesta tarefa, você baixa e inspeciona o código inicial.
Você pode continuar trabalhando por meio do aplicativo DevBytes que você criou no tutorial anterior (se tiver). Alternativamente, você pode baixar o aplicativo inicial.
Nesta tarefa, você baixa e executa o aplicativo inicial e examina o código inicial.
O aplicativo inicial vem com muitos códigos que foram introduzidos no tutorial anterior. O código inicial para
este tutorial tem rede, interface do usuário, cache offline e módulos de repositório. Você pode se concentrar no
agendamento da tarefa em segundo plano usando o WorkManager
.
database
. O pacote contém as entidades do banco de dados e o banco de dados
local, que é implementado usando Room
.repository
. O pacote contém a classe VideosRepository
que
abstrai a camada de dados do resto do aplicativo.WorkManager
é
um dos Componentes de arquitetura
do Android e parte do Android
Jetpack.
WorkManager
é para trabalho em segundo plano que pode ser adiado e requer execução garantida:
Enquanto o WorkManager
executa o trabalho em segundo plano, ele cuida dos problemas de
compatibilidade e das práticas recomendadas para a integridade da bateria e do sistema. WorkManager
oferece compatibilidade de volta ao nível de API 14. WorkManager
escolhe uma maneira apropriada de
agendar uma tarefa em segundo plano, dependendo do nível de API do dispositivo. Ele pode usar JobScheduler
(na API 23 e superior) ou uma combinação de AlarmManager
e BroadcastReceiver
.
O WorkManager
também permite definir critérios de execução da tarefa em segundo plano. Por
exemplo, você pode desejar que a tarefa seja executada apenas quando o status da bateria, o status da rede ou o
estado de carga atenderem a certos critérios. Você aprenderá a definir restrições posteriormente neste tutorial.
Neste tutorial, você programa uma tarefa para obter previamente a lista de reprodução de vídeo DevBytes da rede
uma vez por dia. Para agendar esta tarefa, você usa a biblioteca WorkManager
.
build.gradle (Module:app)
e adicione a dependência WorkManager
ao
projeto.def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"
Antes de adicionar código ao projeto, familiarize-se com as seguintes classes na biblioteca
WorkManager
:
Worker
doWork()
. O método doWork()
é onde você coloca o código a ser
executado em segundo plano, como sincronizar dados com o servidor ou processar imagens. Você implementa o
Worker
nesta tarefa.WorkRequest
WorkRequest
para configurar como e quando executar a tarefa do
trabalhador, com a ajuda de Constraints
como dispositivo conectado ou Wi-Fi conectado.
Você implementa
o WorkRequest
em uma tarefa posterior.WorkManager
WorkRequest
. O
WorkManager
programa as solicitações de trabalho de uma forma que distribua a carga nos recursos
do sistema, enquanto respeita as restrições que você especifica. Você implementa o WorkManager
em
uma tarefa posterior.Nesta tarefa, você adiciona um Worker
para pré-buscar a lista de reprodução de vídeo DevBytes
em segundo
plano.
devbyteviewer
, crie um pacote chamado work
.work
, crie uma classe Kotlin chamada RefreshDataWorker
.RefreshDataWorker
da classe CoroutineWorker
. Passe no
context
e WorkerParameters
como parâmetros do construtor.class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
CoroutineWorker(appContext, params) {
}
doWork()
dentro da classe
RefreshDataWorker
. override suspend fun doWork(): Result {
return Result.success()
}
Uma função de suspensão é uma função que pode ser pausada e retomada posteriormente. Uma função de suspensão pode executar uma operação de longa duração e esperar que ela seja concluída sem bloquear a thread principal.
O método doWork()
dentro da classe Worker
é chamado em uma thread de segundo plano. O
método executa o trabalho de forma síncrona e deve retornar um objeto ListenableWorker.Result
. O sistema Android fornece a um Worker
no máximo 10 minutos para terminar sua execução e retornar um objeto ListenableWorker.Result
. Após
esse tempo expirar, o sistema interrompe à força o Worker
.
Para criar um objeto ListenableWorker.Result
, chame um dos seguintes métodos estáticos para
indicar o status de conclusão do trabalho em segundo plano:
Result.success()
—trabalho concluído com sucesso.Result.failure()
—trabalho concluído com falha permanente.
Result.retry()
- o trabalho encontrou uma falha temporária e
deve ser
tentado novamente.
Nesta tarefa, você implementa o método doWork()
para buscar a lista de reprodução de vídeo
DevBytes da rede. Você pode reutilizar os métodos existentes na classe VideosRepository
para
recuperar os dados da rede.
RefreshDataWorker
, dentro de doWork()
, crie e instancie um objeto
VideosDatabase
e um objeto VideosRepository
. override suspend fun doWork(): Result {
val database = getDatabase(applicationContext)
val repository = VideosRepository(database)
return Result.success()
}
RefreshDataWorker
, dentro de doWork()
, acima da instrução
return
, chame o método refreshVideos()
dentro um bloco try
. Adicione um
registro para rastrear quando o trabalhador é executado.try {
repository.refreshVideos( )
Timber.d("Work request for sync is run")
} catch (e: HttpException) {
return Result.retry()
}
Para resolver o erro de "Referência não resolvida", importe retrofit2.HttpException
.
RefreshDataWorker
completa para sua referência:class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
val database = getDatabase(applicationContext)
val repository = VideosRepository(database)
try {
repository.refreshVideos()
} catch (e: HttpException) {
return Result.retry()
}
return Result.success()
}
}
Um Worker
define uma unidade de trabalho e o WorkRequest
define como e quando o trabalho deve ser executado. Existem duas
implementações concretas da classe WorkRequest
:
OneTimeWorkRequest
é para tarefas únicas. (Uma tarefa
única
acontece apenas uma vez).PeriodicWorkRequest
é para trabalho periódico, trabalho que
se repete em
intervalos. As tarefas podem ser únicas ou periódicas, então escolha a classe de acordo. Para obter mais informações sobre como agendar trabalho recorrente, consulte a documentação de trabalho recorrente.
Nesta tarefa, você define e programa um WorkRequest
para executar o trabalhador que você criou na
tarefa anterior.
Em um aplicativo Android, a classe Application
é a classe base que contém todos os outros
componentes, como atividades e serviços. Quando o processo para seu aplicativo ou pacote é criado, a classe
Application
(ou qualquer subclasse de Application
) é instanciada antes de qualquer
outra classe.
Neste aplicativo de amostra, a classe DevByteApplication
é uma subclasse da classe
Application
. A classe DevByteApplication
é um bom lugar para agendar o
WorkManager
.
DevByteApplication
, crie um método chamado setupRecurringWork()
para
configurar o trabalho recorrente em segundo plano.private fun setupRecurringWork() {
}
setupRecurringWork()
, crie e inicialize uma solicitação de trabalho periódica
para ser executada uma vez por dia, usando o método PeriodicWorkRequestBuilder()
. Passe na classe
RefreshDataWorker
que você criou na tarefa anterior. Passe em um intervalo de repetição de
1
com uma unidade de tempo de TimeUnit.
DAYS
.val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
.build()
Para resolver o erro, importe java.util.concurrent.TimeUnit
.
Depois de definir seu WorkRequest
, você pode agendá-lo com o WorkManager
, usando o
método enqueueUniquePeriodicWork()
. Este método permite que você
adicione um nome
exclusivo PeriodicWorkRequest
à fila, onde apenas um
PeriodicWorkRequest
de um determinado nome pode estar ativo por vez.
Por exemplo, você pode querer que apenas uma operação de sincronização esteja ativa. Se uma operação de sincronização estiver pendente, você pode optar por deixá-la ser executada ou substituí-la por seu novo trabalho, usando uma ExistingPeriodicWorkPolicy.
Para aprender mais sobre as maneiras de agendar um WorkRequest
, consulte a documentação WorkManager
.
RefreshDataWorker
, no início da classe, adicione um objeto complementar. Defina um
nome de trabalho para identificar exclusivamente este trabalhador.companion object {
const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
DevByteApplication
, no final do método setupRecurringWork()
, programe o
trabalho usando o método enqueueUniquePeriodicWork()
. Passe o KEEP
enum para a
ExistingPeriodicWorkPolicy. Passe repeatingRequest
como o parâmetro
PeriodicWorkRequest
.WorkManager.getInstance().enqueueUniquePeriodicWork(
RefreshDataWorker.WORK_NAME,
ExistingPeriodicWorkPolicy.KEEP,
repeatingRequest)
Se houver trabalho pendente (não concluído) com o mesmo nome, o parâmetro
ExistingPeriodicWorkPolicy.
KEEP
faz com que o
WorkManager
mantenha o trabalho periódico anterior e descartar a nova solicitação de trabalho.
DevByteApplication
, crie um objeto CoroutineScope
. Passe em Dispatchers.Default
como o parâmetro do construtor.private val applicationScope = CoroutineScope(Dispatchers.Default)
DevByteApplication
, adicione um novo método chamado delayedInit()
para
iniciar uma corrotina.private fun delayedInit() {
applicationScope.launch {
}
}
delayedInit()
, chame setupRecurringWork()
.onCreate()
para o método delayedInit()
.
private fun delayedInit() {
applicationScope.launch {
Timber.plant(Timber.DebugTree())
setupRecurringWork()
}
}
DevByteApplication
, no final do método onCreate()
, adicione uma chamada
ao método delayedInit()
.override fun onCreate() {
super.onCreate()
delayedInit()
}
RefreshDataWorker
.WorkManager
agenda seu trabalho recorrente imediatamente. D/RefreshDataWorker: Work request for sync is run I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]
O registro WM-WorkerWrapper
é exibido na biblioteca WorkManager
, portanto, você não
pode alterar esta mensagem de log.
Nesta etapa, você diminui o intervalo de tempo de 1 dia para 15 minutos. Faça isso para ver os registros de uma solicitação de trabalho periódico em ação.
DevByteApplication
, dentro do método setupRecurringWork()
, comente a
definição repeatingRequest
atual. Adicione uma nova solicitação de trabalho com
um intervalo de repetição periódica de 15
minutos. // val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
// .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
.build()
RefreshDataWorker
. Para limpar
os registros anteriores, clique no ícone Clear logcat.WorkManager
agenda seu trabalho recorrente imediatamente. No painel
Logcat, observe os registros - a solicitação de trabalho é executada uma vez a cada 15
minutos. Aguarde 15 minutos para ver outro conjunto de registros de solicitação de trabalho. Você pode deixar
o aplicativo em execução ou fechá-lo; o gerenciador de trabalho ainda deve ser executado.12:44:40 D/RefreshDataWorker: Work request for sync is run 12:44:40 I/WM-WorkerWrapper: Worker result SUCCESS for Work 12:59:24 D/RefreshDataWorker: Work request for sync is run 12:59:24 I/WM-WorkerWrapper: Worker result SUCCESS for Work 13:15:03 D/RefreshDataWorker: Work request for sync is run 13:15:03 I/WM-WorkerWrapper: Worker result SUCCESS for Work 13:29:22 D/RefreshDataWorker: Work request for sync is run 13:29:22 I/WM-WorkerWrapper: Worker result SUCCESS for Work 13:44:26 D/RefreshDataWorker: Work request for sync is run 13:44:26 I/WM-WorkerWrapper: Worker result SUCCESS for Work
Parabéns! Você criou um trabalhador e agendou a solicitação de trabalho com WorkManager
. Mas há um
problema: Você não especificou nenhuma restrição. O WorkManager
agendará o trabalho uma vez por
dia, mesmo se o dispositivo estiver com pouca bateria, hibernando ou não tiver conexão de rede. Isso afetará a
bateria e o desempenho do dispositivo e pode resultar em uma experiência do usuário insatisfatória.
Em sua próxima tarefa, você resolverá esse problema adicionando restrições.
Na tarefa anterior, você usou o WorkManager
para agendar uma solicitação de trabalho. Nesta
tarefa, você adiciona critérios para quando executar o trabalho.
Ao definir o WorkRequest
, você pode especificar restrições para quando o Worker
deve
ser executado. Por exemplo, você pode especificar que o trabalho deve ser executado apenas quando o dispositivo
estiver ocioso ou apenas quando o dispositivo estiver conectado e conectado ao Wi-Fi. Você também pode
especificar uma política de retirada para repetir o trabalho. As restrições suportadas são os métodos definidos em
Constraints.Builder
. Para
saber mais, consulte Definindo suas solicitações de trabalho.
Nesta etapa, você cria um objeto Constraints
e define uma restrição no objeto, uma restrição do
tipo rede. (É mais fácil notar os registros com apenas uma restrição. Em uma etapa posterior, você adiciona
outras restrições).
DevByteApplication
, no início de setupRecurringWork()
, defina um
val
do tipo Constraints
. Use o método Constraints.Builder()
.
val constraints = Constraints.Builder()
Para resolver o erro, importe androidx.work.Constraints
.
setRequiredNetworkType()
para adicionar uma restrição de
tipo de rede ao
objeto constraints
. Use o enum UNMETERED
para que a solicitação de trabalho somente
seja executada quando o dispositivo estiver em uma rede ilimitada..setRequiredNetworkType(NetworkType.UNMETERED)
build()
para gerar as restrições do construtor. val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.build()
Agora você precisa definir o objeto Constraints
recém-criado para a solicitação de trabalho.
DevByteApplication
, dentro do método setupRecurringWork()
, defina o
objeto Constraints
para a solicitação de trabalho periódico, repeatingRequest
. Para
definir as restrições, adicione o método setConstraints()
acima da chamada do método
build()
. val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
.setConstraints(constraints)
.build()
Nesta etapa, você executa o aplicativo e observa a solicitação de trabalho restrita sendo executada em segundo plano em intervalos.
work
.WorkManager
agenda a tarefa
em segundo plano imediatamente. Como a restrição da rede não é atendida, a tarefa não é executada.11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled 11:31:47 D/RefreshDataWorker: Work request for sync is run 11:31:47 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 11:46:45 D/RefreshDataWorker: Work request for sync is run 11:46:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 12:03:05 D/RefreshDataWorker: Work request for sync is run 12:03:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 12:16:45 D/RefreshDataWorker: Work request for sync is run 12:16:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 12:31:45 D/RefreshDataWorker: Work request for sync is run 12:31:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 12:47:05 D/RefreshDataWorker: Work request for sync is run 12:47:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 13:01:45 D/RefreshDataWorker: Work request for sync is run 13:01:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]
Nesta etapa, você adiciona as seguintes restrições ao PeriodicWorkRequest
:
Implemente o seguinte na classe DevByteApplication
.
DevByteApplication
, dentro do método setupRecurringWork()
, indique que a
solicitação de trabalho deve ser executada apenas se a bateria não estiver fraca. Adicione a restrição antes
da chamada do método build()
e use o método setRequiresBatteryNotLow()
. .setRequiresBatteryNotLow(true)
build()
e use o método setRequiresCharging()
..setRequiresCharging(true)
build()
e use o método setRequiresDeviceIdle()
. Essa restrição executa a
solicitação de trabalho
apenas quando o usuário não está usando ativamente o dispositivo. Este recurso está disponível apenas no
Android 6.0 (Marshmallow) e superior, portanto, adicione uma condição para a versão do SDK M
e
superior..apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setRequiresDeviceIdle(true)
}
}
Aqui está a definição completa do objeto constraints
.
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresBatteryNotLow(true)
.setRequiresCharging(true)
.apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setRequiresDeviceIdle(true)
}
}
.build()
setupRecurringWork()
, altere o intervalo de solicitação de volta para uma vez
por dia.val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
.setConstraints(constraints)
.build()
Aqui está a implementação completa do método setupRecurringWork()
, com um registro para que você
possa rastrear quando a solicitação de trabalho periódico é agendada.
private fun setupRecurringWork() {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresBatteryNotLow(true)
.setRequiresCharging(true)
.apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setRequiresDeviceIdle(true)
}
}
.build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
.setConstraints(constraints)
.build()
Timber.d("Periodic Work request for sync is scheduled")
WorkManager.getInstance().enqueueUniquePeriodicWork(
RefreshDataWorker.WORK_NAME,
ExistingPeriodicWorkPolicy.KEEP,
repeatingRequest)
}
WorkManager
agenda imediatamente a solicitação de trabalho. A
solicitação de trabalho é executada uma vez por dia, quando todas as restrições são atendidas.Bom trabalho! Você implementou e programou uma solicitação de trabalho compatível com a bateria para a
pré-busca diária de vídeos no aplicativo DevBytes. O WorkManager
agendará e executará o trabalho,
otimizando os recursos do sistema. Seus usuários e suas baterias ficarão muito felizes.
Projeto Android Studio: DevBytesWorkManager.
WorkManager
facilita o agendamento de tarefas assíncronas adiáveis que
devem ser executadas de forma confiável.WorkManager
.WorkManager
são Worker
, WorkRequest
e WorkManager
.Worker
representa uma unidade de trabalho. Para implementar a tarefa em segundo plano,
estenda a classe Worker
e substitua o método doWork()
.WorkRequest
representa uma solicitação para executar uma unidade de trabalho.
WorkRequest
é a classe base para especificar parâmetros de trabalho que você agenda no
WorkManager
. WorkRequest
: OneTimeWorkRequest
para tarefas únicas e PeriodicWorkRequest
para solicitações de trabalho periódicas.WorkRequest
, você pode especificar Constraints
indicando quando o Worker
deve ser executado. As
restrições incluem algo como se o dispositivo está conectado, se o dispositivo está ocioso ou se o Wi-Fi está
conectado.WorkRequest
, use os métodos definidos listados na documentação do Constraints.Builder
. Por exemplo, para
indicar que o
WorkRequest
não deve ser executado se a bateria do dispositivo estiver fraca, use o método de
configuração setRequiresBatteryNotLow()
.WorkRequest
, passe a tarefa para o sistema Android. Para fazer isso, agende
a tarefa usando um dos métodos do WorkManager
enqueue
.Worker
é executado depende das restrições que são usadas no
WorkRequest
e das otimizações do sistema. O WorkManager
foi projetado para fornecer
o melhor comportamento possível, dadas essas restrições.Documentação para desenvolvimento em Android:
Esta seção lista as possíveis tarefas de casa para os alunos que estão trabalhando neste tutorial como parte de um curso ministrado por um instrutor.
Quais são as implementações concretas da classe WorkRequest
?
▢ OneTimeWorkPeriodicRequest
▢ OneTimeWorkRequest
e PeriodicWorkRequest
▢ OneTimeWorkRequest
e RecurringWorkRequest
▢ OneTimeOffWorkRequest
e RecurringWorkRequest
Qual das seguintes classes o WorkManager
usa para agendar a tarefa em segundo plano na API 23 e
superior?
▢ Apenas JobScheduler
▢ BroadcastReceiver
e AlarmManager
▢ AlarmManager
e JobScheduler
▢ Scheduler
e BroadcastReceiver
Qual API você usa para adicionar restrições a um WorkRequest
?
▢ setConstraints()
▢ addConstraints()
▢ setConstraint()
▢ addConstraintsToWorkRequest()
Para obter enlaces para outros tutoriais neste curso, consulte a página de destino dos tutoriais Fundamentos de Android em Kotlin.