1. Introdução
Este codelab ensina conceitos avançados do WorkManager. Ele se baseia no material básico abrangido no codelab Trabalho em segundo plano com o WorkManager.
Veja outros recursos disponíveis para se familiarizar com o WorkManager:
- Guia do WorkManager
- Série do blog: Apresentação do WorkManager
- Palestra da Conferência de Desenvolvedores Android 2019: WorkManager: além do básico
- WorkManager: série MAD Skills
O que você vai criar
Neste codelab, você vai trabalhar no Blur-O-Matic, um app que desfoca fotos e imagens e salva o resultado em um arquivo. Se você já concluiu o codelab Trabalho em segundo plano com o WorkManager, esse é um app de exemplo semelhante. A única diferença é que ele permite selecionar sua própria imagem da galeria de fotos para desfocar. Aqui, você vai adicionar alguns recursos ao código:
- Configuração personalizada
- Usar a API Progress para atualizar a IU enquanto o trabalho é executado
- Testar os workers
Pré-requisitos
Para fazer este codelab, você precisará da versão estável mais recente do Android Studio.
Você também já deve conhecer LiveData
, ViewModel
e View Binding
. Caso você ainda não conheça essas classes, confira o codelab Componentes compatíveis com ciclo de vida do Android (especificamente para ViewModel e LiveData) ou o codelab Room com View (uma apresentação dos componentes de arquitetura).
Se você não entender algum ponto
Se você não entender algum ponto deste codelab ou quiser ver o estado final do código,
Se preferir, clone o codelab do WorkManager concluído no GitHub:
$ git clone -b advanced https://github.com/googlecodelabs/android-workmanager
2. Etapas da configuração
Etapa 1: fazer o download do código
Clique no link a seguir para fazer o download da versão do código que será usada neste codelab:
Se preferir, clone o codelab no GitHub:
$ git clone -b advanced_start https://github.com/googlecodelabs/android-workmanager
Etapa 2: executar o app
Execute o app. Você verá as seguintes telas. Conceda permissão ao app para acessar suas fotos quando solicitado.
É possível selecionar uma imagem e ir para a próxima tela, que tem botões de opção onde você pode selecionar o nível de desfoque da imagem. Pressionar o botão Ir desfoca e salva a imagem. Durante o desfoque, o app mostra um botão Cancel para que você finalize o trabalho.
O código inicial contém o seguinte:
WorkerUtils
: essa classe contém o código para desfoque e alguns métodos práticos que você vai usar posteriormente para mostrarNotifications
e deixar o app mais lento.BlurApplication
: classe do app com um métodoonCreate()
simples para inicializar o sistema de geração de registros Timber para builds de depuração.BlurActivity
: a atividade que mostra a imagem e inclui botões de opção para selecionar o nível de desfoque.BlurViewModel
: esse modelo de visualização armazena todos os dados necessários para mostrar aBlurActivity
. Também será a classe em que você vai iniciar o trabalho em segundo plano usando o WorkManager.Workers/CleanupWorker
: esse worker sempre exclui os arquivos temporários, se houver.Workers/BlurWorker
: esse worker desfoca a imagem transmitida como dados de entrada com um URI e retorna o URI do arquivo temporário.Workers/SaveImageToFileWorker
: esse worker usa como entrada o URI da imagem temporária e retorna o URI do arquivo final.Constants
: uma classe estática com algumas constantes que serão usadas durante o codelab.SelectImageActivity
: a primeira atividade que permite selecionar uma imagem.res/activity_blur.xml
eres/activity_select.xml
: arquivos de layout de cada atividade.
Você vai fazer mudanças no código nas seguintes classes: BlurApplication
, BlurActivity
, BlurViewModel
e BlurWorker
.
3. Adicionar o WorkManager ao app
O WorkManager
requer a dependência do Gradle abaixo. Os itens a seguir já foram incluídos nos arquivos:
app/build.gradle
dependencies {
implementation "androidx.work:work-runtime-ktx:$versions.work"
}
Você precisa ter a versão mais atual do work-runtime
, disponível na página de versões do WorkManager, e aplicar uma delas à versão estável mais recente ou usar uma das opções abaixo:
build.gradle
versions.work = "2.7.1"
Clique em Sync Now para sincronizar o projeto com os arquivos do Gradle modificados.
4. Adicionar uma configuração personalizada do WorkManager
Nesta etapa, você vai adicionar uma configuração personalizada ao app para modificar o nível de geração de registros do WorkManager para builds de depuração.
Etapa 1: desativar a inicialização padrão
Conforme descrito na documentação de Configuração e inicialização personalizadas do WorkManager, é preciso desativar a inicialização padrão no arquivo AndroidManifest.xml
, removendo o nó mesclado automaticamente da biblioteca do WorkManager por padrão.
Para remover esse nó, você pode adicionar um novo nó de provedor ao AndroidManifest.xml
, conforme mostrado abaixo:
AndroidManifest.xml
<application
...
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
</application>
Você também precisará adicionar o namespace das ferramentas ao manifesto. O arquivo completo com essas mudanças será:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<manifest package="com.example.background"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".BlurApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".SelectImageActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".BlurActivity" />
<!-- ADD THE FOLLOWING NODE -->
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
</application>
</manifest>
Etapa 2: adicionar um Configuration.Provider à classe Application
Você pode usar uma inicialização sob demanda implementando a interface Configuration.Provider
do WorkManager na classe Application
. Na primeira vez que o app receber a instância do WorkManager usando getInstance(context)
, o WorkManager será inicializado usando a configuração retornada por getWorkManagerConfiguration()
.
BlurApplication.kt
class BlurApplication : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.build()
...
}
Com essa mudança, o WorkManager é executado com a geração de registros definida como DEBUG
.
Uma opção melhor provavelmente será configurar o WorkManager dessa maneira apenas para builds de depuração do app, usando algo como:
BlurApplication.kt
class BlurApplication() : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration {
return if (BuildConfig.DEBUG) {
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.build()
} else {
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.ERROR)
.build()
}
}
...
}
Em seguida, BlurApplication.kt completo se torna:
BlurApplication.kt
/* Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
package com.example.background
import android.app.Application
import androidx.work.Configuration
import timber.log.Timber
import timber.log.Timber.DebugTree
class BlurApplication() : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration {
return if (BuildConfig.DEBUG) {
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.build()
} else {
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.ERROR)
.build()
}
}
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Timber.plant(DebugTree())
}
}
}
Etapa 3: executar o app no modo de depuração
O WorkManager agora é configurado para que os builds de depuração registrem todas as mensagens da biblioteca.
Ao executar o app, é possível ver os registros na guia logcat
do Android Studio:
Etapa 4: o que você pode configurar?
A lista completa de parâmetros está no guia de referência do WorkManager para Configuration.Builder
. Preste atenção a dois parâmetros adicionais:
WorkerFactory
- Intervalo
JobId
Modificar o WorkerFactory
permite adicionar outros parâmetros ao construtor do worker. Mais informações sobre como implementar um WorkerFactory personalizado estão disponíveis neste artigo sobre Como personalizar o WorkManager. Se você está usando o WorkManager e a API JobScheduler
no app, é recomendável personalizar o intervalo JobId
para evitar que o mesmo seja usado pelas duas APIs.
Compartilhamento do progresso do WorkManager
O WorkManager v2.3 adicionou a funcionalidade para compartilhar informações de progresso do worker com o app usando o setProgressAsync()
, ou setProgress()
quando usado em um CoroutineWorker
. Essas informações podem ser observadas por uma classe WorkInfo e serão usadas para fornecer feedback ao usuário na IU. Os dados de progresso são cancelados quando o worker alcança um estado final (SUCCEEDED, FAILED ou CANCELLED). Para saber mais sobre como publicar e ouvir o progresso, leia Como observar o progresso intermediário do worker.
O que você fará agora é adicionar uma barra de progresso na IU para que, se o app estiver em primeiro plano, o usuário possa ver como está o desfoque. O resultado final será assim:
Etapa 1: modificar a ProgressBar
Para modificar a ProgressBar no layout, você precisa excluir o parâmetro android:indeterminate="true"
, adicionar o estilo style="@android:style/Widget.ProgressBar.Horizontal",
e definir um valor inicial com android:progress="0"
. Também é necessário definir a orientação de LinearLayout
como "vertical"
:
app/src/main/res/layout/activity_blur.xml
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="0"
android:visibility="gone"
android:layout_gravity="center_horizontal"
/>
<Button
android:id="@+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel_work"
android:visibility="gone"
/>
</LinearLayout>
A outra mudança necessária é garantir que a ProgressBar
seja reiniciada na posição inicial. Faça isso atualizando a função showWorkFinished()
no arquivo BlurActivity.kt
:
app/src/main/java/com/example/background/BlurActivity.kt
/**
* Shows and hides views for when the Activity is done processing an image
*/
private fun showWorkFinished() {
with(binding) {
progressBar.visibility = View.GONE
cancelButton.visibility = View.GONE
goButton.visibility = View.VISIBLE
progressBar.progress = 0 // <-- ADD THIS LINE
}
}
Etapa 2: observar as informações de progresso no ViewModel
Já existe um observador no arquivo BlurViewModel
que verifica quando a cadeia está completa. Adicione um novo para observar o progresso postado por BlurWorker
.
Primeiro, adicione algumas constantes para rastrear isso no final do arquivo Constants.kt
:
app/src/main/java/com/example/background/Constants.kt
// Progress Data Key
const val PROGRESS = "PROGRESS"
const val TAG_PROGRESS = "TAG_PROGRESS"
A próxima etapa é adicionar essa tag ao WorkRequest
do BlurWorker
no arquivo BlurViewModel.kt
para que você possa recuperar WorkInfo
. A partir de WorkInfo
, é possível recuperar as informações de progresso do worker:
app/src/main/java/com/example/background/BlurViewModel.kt
// Add WorkRequests to blur the image the number of times requested
for (i in 0 until blurLevel) {
val blurBuilder = OneTimeWorkRequestBuilder<BlurWorker>()
// Input the Uri if this is the first blur operation
// After the first blur operation the input will be the output of previous
// blur operations.
if (i == 0) {
blurBuilder.setInputData(createInputDataForUri())
}
blurBuilder.addTag(TAG_PROGRESS) // <-- ADD THIS
continuation = continuation.then(blurBuilder.build())
}
Adicione um novo LiveData
ao arquivo BlurViewModel.kt
que rastreie esse WorkRequest
e inicialize o LiveData
no bloco init
:
app/src/main/java/com/example/background/BlurViewModel.kt
class BlurViewModel(application: Application) : AndroidViewModel(application) {
internal var imageUri: Uri? = null
internal var outputUri: Uri? = null
internal val outputWorkInfoItems: LiveData<List<WorkInfo>>
internal val progressWorkInfoItems: LiveData<List<WorkInfo>> // <-- ADD THIS
private val workManager: WorkManager = WorkManager.getInstance(application)
init {
// This transformation makes sure that whenever the current work Id changes the WorkStatus
// the UI is listening to changes
outputWorkInfoItems = workManager.getWorkInfosByTagLiveData(TAG_OUTPUT)
progressWorkInfoItems = workManager.getWorkInfosByTagLiveData(TAG_PROGRESS) // <-- ADD THIS
}
...
}
Etapa 3: observar o LiveData na atividade
Agora você pode usar esse LiveData
em BlurActivity
para observar todo o progresso publicado. Primeiro, registre um novo observador LiveData
no final do método onCreate()
:
app/src/main/java/com/example/background/BlurActivity.kt
// Show work status
viewModel.outputWorkInfoItems.observe(this, outputObserver())
// ADD THE FOLLOWING LINES
// Show work progress
viewModel.progressWorkInfoItems.observe(this, progressObserver())
Agora, você pode verificar a WorkInfo
recebida no observador para ver se há informações de progresso e atualizar a ProgressBar
conforme necessário:
app/src/main/java/com/example/background/BlurActivity.kt
private fun progressObserver(): Observer<List<WorkInfo>> {
return Observer { listOfWorkInfo ->
if (listOfWorkInfo.isNullOrEmpty()) {
return@Observer
}
listOfWorkInfo.forEach { workInfo ->
if (WorkInfo.State.RUNNING == workInfo.state) {
val progress = workInfo.progress.getInt(PROGRESS, 0)
binding.progressBar.progress = progress
}
}
}
}
Etapa 4: publicar o progresso de BlurWorker
Todas as partes necessárias para exibir as informações de progresso já estão configuradas. É hora de adicionar a publicação real das informações de progresso ao BlurWorker
.
Este exemplo simplesmente simula um processo longo na função doWork()
para publicar informações de progresso durante um período definido.
A mudança feita aqui é trocar um único atraso por 10 menores, definindo um novo progresso em cada iteração:
app/src/main/java/com/example/background/workers/BlurWorker.kt
override fun doWork(): Result {
val appContext = applicationContext
val resourceUri = inputData.getString(KEY_IMAGE_URI)
makeStatusNotification("Blurring image", appContext)
// sleep()
(0..100 step 10).forEach {
setProgressAsync(workDataOf(PROGRESS to it))
sleep()
}
...
}
Como o atraso original era de 3 segundos, provavelmente é uma boa ideia reduzi-lo para 10 de 0,3 segundos:
app/src/main/java/com/example/background/Constants.kt
// const val DELAY_TIME_MILLIS: Long = 3000
const val DELAY_TIME_MILLIS: Long = 300
Etapa 5: executar
A execução do app nesse ponto deve mostrar a ProgressBar preenchida com as mensagens provenientes de BlurWorker
.
5. Testes do WorkManager
Testes são componentes importantes de todos os apps e, ao introduzir uma biblioteca como o WorkManager, é importante fornecer ferramentas para testar o código com facilidade.
Com o WorkManager, também disponibilizamos alguns auxiliares para testar facilmente os workers. Para saber mais sobre como criar testes para os workers, consulte a documentação do WorkManager sobre testes.
Nesta seção do codelab, apresentaremos alguns testes para as classes do worker com alguns dos casos de uso comuns.
Primeiro, vamos fornecer uma maneira fácil de configurar os testes. Para isso, podemos criar uma TestRule que configura o WorkManager:
- Adicione dependências
- Crie
WorkManagerTestRule
eTestUtils
- Crie um teste para
CleanupWorker
- Crie um teste para
BlurWorker
Se você já criou a pasta AndroidTest no seu projeto, precisamos adicionar algumas dependências para usar nos testes:
app/build.gradle
androidTestImplementation "androidx.arch.core:core-testing:2.1.0"
androidTestImplementation "androidx.test.ext:junit:1.1.3"
androidTestImplementation "androidx.test:rules:1.4.0"
androidTestImplementation "androidx.test:runner:1.4.0"
androidTestImplementation "androidx.work:work-testing:$versions.work"
Agora, podemos começar juntar essas partes com uma TestRule que podemos usar nos testes:
app/src/androidTest/java/com/example/background/workers/WorkManagerTestRule.kt
/* Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
package com.example.background.workers
import android.content.Context
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
import androidx.work.Configuration
import androidx.work.WorkManager
import androidx.work.testing.SynchronousExecutor
import androidx.work.testing.WorkManagerTestInitHelper
import org.junit.rules.TestWatcher
import org.junit.runner.Description
class WorkManagerTestRule : TestWatcher() {
lateinit var targetContext: Context
lateinit var testContext: Context
lateinit var configuration: Configuration
lateinit var workManager: WorkManager
override fun starting(description: Description?) {
targetContext = InstrumentationRegistry.getInstrumentation().targetContext
testContext = InstrumentationRegistry.getInstrumentation().context
configuration = Configuration.Builder()
// Set log level to Log.DEBUG to make it easier to debug
.setMinimumLoggingLevel(Log.DEBUG)
// Use a SynchronousExecutor here to make it easier to write tests
.setExecutor(SynchronousExecutor())
.build()
// Initialize WorkManager for instrumentation tests.
WorkManagerTestInitHelper.initializeTestWorkManager(targetContext, configuration)
workManager = WorkManager.getInstance(targetContext)
}
}
Como precisaremos dessa imagem de teste no dispositivo (onde os testes serão executados), podemos criar algumas funções auxiliares para usar nos testes:
app/src/androidTest/java/com/example/background/workers/TestUtils.kt
/* Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
package com.example.background.workers
import android.content.Context
import android.graphics.BitmapFactory
import android.net.Uri
import com.example.background.OUTPUT_PATH
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.util.UUID
/**
* Copy a file from the asset folder in the testContext to the OUTPUT_PATH in the target context.
* @param testCtx android test context
* @param targetCtx target context
* @param filename source asset file
* @return Uri for temp file
*/
@Throws(Exception::class)
fun copyFileFromTestToTargetCtx(testCtx: Context, targetCtx: Context, filename: String): Uri {
// Create test image
val destinationFilename = String.format("blur-test-%s.png", UUID.randomUUID().toString())
val outputDir = File(targetCtx.filesDir, OUTPUT_PATH)
if (!outputDir.exists()) {
outputDir.mkdirs()
}
val outputFile = File(outputDir, destinationFilename)
val bis = BufferedInputStream(testCtx.assets.open(filename))
val bos = BufferedOutputStream(FileOutputStream(outputFile))
val buf = ByteArray(1024)
bis.read(buf)
do {
bos.write(buf)
} while (bis.read(buf) != -1)
bis.close()
bos.close()
return Uri.fromFile(outputFile)
}
/**
* Check if a file exists in the given context.
* @param testCtx android test context
* @param uri for the file
* @return true if file exist, false if the file does not exist of the Uri is not valid
*/
fun uriFileExists(targetCtx: Context, uri: String?): Boolean {
if (uri.isNullOrEmpty()) {
return false
}
val resolver = targetCtx.contentResolver
// Create a bitmap
try {
BitmapFactory.decodeStream(
resolver.openInputStream(Uri.parse(uri)))
} catch (e: FileNotFoundException) {
return false
}
return true
}
Quando terminarmos esse trabalho, começaremos a desenvolver os testes.
Primeiro, testamos o CleanupWorker
para verificar se ele realmente exclui os arquivos. Para fazer isso, copie a imagem de teste no dispositivo do teste e verifique se ela está lá após a execução do CleanupWorker
:
app/src/androidTest/java/com/example/background/workers/CleanupWorkerTest.kt
/* Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
package com.example.background.workers
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkInfo
import org.hamcrest.CoreMatchers.`is`
import org.junit.Assert.assertThat
import org.junit.Rule
import org.junit.Test
class CleanupWorkerTest {
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
@get:Rule
var wmRule = WorkManagerTestRule()
@Test
fun testCleanupWork() {
val testUri = copyFileFromTestToTargetCtx(
wmRule.testContext, wmRule.targetContext, "test_image.png")
assertThat(uriFileExists(wmRule.targetContext, testUri.toString()), `is`(true))
// Create request
val request = OneTimeWorkRequestBuilder<CleanupWorker>().build()
// Enqueue and wait for result. This also runs the Worker synchronously
// because we are using a SynchronousExecutor.
wmRule.workManager.enqueue(request).result.get()
// Get WorkInfo
val workInfo = wmRule.workManager.getWorkInfoById(request.id).get()
// Assert
assertThat(uriFileExists(wmRule.targetContext, testUri.toString()), `is`(false))
assertThat(workInfo.state, `is`(WorkInfo.State.SUCCEEDED))
}
}
Agora, você pode executar este teste no Android Studio no menu "Executar" ou usando o retângulo verde no lado esquerdo da classe de teste:
Também é possível executar testes na linha de comando usando o comando ./gradlew cAT
da pasta raiz do projeto.
Os testes devem ser executados corretamente.
Em seguida, podemos testar o BlurWorker. Esse worker espera dados de entrada com o URI da imagem para o processamento. Desse modo, podemos criar alguns testes: um que verifica se o worker falha quando não há URI de entrada e um que realmente processa a imagem de entrada.
app/src/androidTest/java/com/example/background/workers/BlurWorkerTest.kt
/* Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
package com.example.background.workers
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkInfo
import androidx.work.workDataOf
import org.hamcrest.CoreMatchers.`is`
import org.junit.Assert.assertThat
import org.junit.Rule
import com.example.background.KEY_IMAGE_URI
import org.junit.Test
class BlurWorkerTest {
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
@get:Rule
var wmRule = WorkManagerTestRule()
@Test
fun testFailsIfNoInput() {
// Define input data
// Create request
val request = OneTimeWorkRequestBuilder<BlurWorker>().build()
// Enqueue and wait for result. This also runs the Worker synchronously
// because we are using a SynchronousExecutor.
wmRule.workManager.enqueue(request).result.get()
// Get WorkInfo
val workInfo = wmRule.workManager.getWorkInfoById(request.id).get()
// Assert
assertThat(workInfo.state, `is`(WorkInfo.State.FAILED))
}
@Test
@Throws(Exception::class)
fun testAppliesBlur() {
// Define input data
val inputDataUri = copyFileFromTestToTargetCtx(
wmRule.testContext,
wmRule.targetContext,
"test_image.png")
val inputData = workDataOf(KEY_IMAGE_URI to inputDataUri.toString())
// Create request
val request = OneTimeWorkRequestBuilder<BlurWorker>()
.setInputData(inputData)
.build()
// Enqueue and wait for result. This also runs the Worker synchronously
// because we are using a SynchronousExecutor.
wmRule.workManager.enqueue(request).result.get()
// Get WorkInfo
val workInfo = wmRule.workManager.getWorkInfoById(request.id).get()
val outputUri = workInfo.outputData.getString(KEY_IMAGE_URI)
// Assert
assertThat(uriFileExists(wmRule.targetContext, outputUri), `is`(true))
assertThat(workInfo.state, `is`(WorkInfo.State.SUCCEEDED))
}
}
Se você executar esses testes, eles vão funcionar.
6. Parabéns
Parabéns! Você concluiu o app Blu-O-Matic e, no processo, aprendeu a:
- Criar uma configuração personalizada
- Publicar o progresso do worker
- Exibir o progresso do trabalho na IU
- Programar testes para workers
Bom "trabalho"! Para ver o estado final do código e todas as modificações, confira:
Se preferir, você pode clonar o codelab do WorkManager no GitHub:
$ git clone -b advanced https://github.com/googlecodelabs/android-workmanager
O WorkManager envolve muito mais do que o conteúdo abordado neste codelab. Para saber mais, consulte a documentação do WorkManager.