1. Introducción
En este codelab, se enseñan conceptos avanzados de WorkManager. Se creó a partir del material abarcado en el codelab Trabajo en segundo plano con WorkManager.
A continuación, se incluyen otros recursos disponibles para que te familiarices con WorkManager:
- Guía de WorkManager
- Serie de blogs: Introducción a WorkManager
- Charla de ADS 2019 sobre WorkManager: WorkManager: Conceptos más avanzados
- WorkManager: Serie de MAD Skills
Qué compilarás
En este codelab, trabajarás en Blur-O-Matic, una app que desenfoca imágenes y fotos, y guarda el resultado en un archivo. Si ya completaste el codelab de Trabajo en segundo plano con WorkManager, verás que esta es una app de ejemplo similar (la única diferencia es que esta te permite seleccionar tu propia imagen de la galería de fotos para desenfocar). Ahora, agregarás algunas funciones al código:
- Configuración personalizada
- API de progreso para actualizar la IU mientras se ejecuta tu trabajo
- Prueba de tus trabajadores
Requisitos
Para realizar este codelab, necesitarás la versión estable de Android Studio más reciente.
También debes estar familiarizado con LiveData
, ViewModel
y View Binding
. Si no estás familiarizado con estas clases, consulta el Codelab de componentes optimizados para ciclos de vida de Android (en especial para ViewModel y LiveData) o el Codelab sobre Room con una View (introducción a los componentes de la arquitectura).
Si en algún momento no puedes avanzar
Si en algún momento no puedes avanzar con este codelab o si deseas ver el estado final del código, puedes
Si lo prefieres, también puedes clonar el codelab de WorkManager completado desde GitHub:
$ git clone -b advanced https://github.com/googlecodelabs/android-workmanager
2. Cómo prepararte
Paso 1: Descarga el código
Haz clic en el siguiente vínculo para descargar la versión del código que deberás seguir durante este codelab:
Si lo prefieres, también puedes clonar el codelab desde GitHub:
$ git clone -b advanced_start https://github.com/googlecodelabs/android-workmanager
Paso 2: Ejecuta la app
Ejecuta la app. Deberías ver las siguientes pantallas: Asegúrate de darle permiso a la app para que acceda a tus fotos cuando se te solicite.
Puedes seleccionar una imagen e ir a la pantalla siguiente, que tiene botones de selección con los que podrás elegir cuánto desenfocar tu imagen. Si presionas el botón Go, la imagen se desenfocará y se guardará. Durante el proceso, la app muestra el botón Cancel para que puedas detener el trabajo.
El código inicial contiene lo siguiente:
WorkerUtils
: Esta clase contiene el código de la acción de desenfoque y algunos métodos útiles que usarás más tarde para mostrarNotifications
y ralentizar la app.BlurApplication
: Es la clase de aplicación con un métodoonCreate()
simple para inicializar el sistema de registro Timber para compilaciones de depuración.BlurActivity
: Esta es la actividad que muestra la imagen y que incluye botones de selección para elegir el grado de desenfoque.BlurViewModel
: Este modelo de vista almacena todos los datos necesarios a fin de mostrar laBlurActivity
. También será la clase en la que inicies el trabajo en segundo plano con WorkManager.Workers/CleanupWorker
: Este trabajador siempre borra los archivos temporales que existan.Workers/BlurWorker
: Este trabajador desenfoca la imagen que se pasa como datos de entrada con un URI y muestra el URI del archivo temporal.Workers/SaveImageToFileWorker
: Este trabajador toma como entrada el URI de la imagen temporal y muestra el URI del archivo final.Constants
: Esta es una clase estática con algunas constantes que usarás durante el codelab.SelectImageActivity
: Esta es la primera actividad que te permitirá seleccionar una imagen.res/activity_blur.xml
yres/activity_select.xml
: Estos son los archivos de diseño de cada actividad.
Realizarás cambios en el código en las siguientes clases: BlurApplication
, BlurActivity
, BlurViewModel
y BlurWorker
.
3. Agrega WorkManager a tu app
WorkManager
requiere la dependencia de Gradle que se indica a continuación. Estas ya se incluyeron en los archivos:
app/build.gradle
dependencies {
implementation "androidx.work:work-runtime-ktx:$versions.work"
}
Debes obtener la versión más reciente de work-runtime
en la página de la versión de WorkManager e indicar el número de la versión estable más reciente o usar la que se indica a continuación:
build.gradle
versions.work = "2.7.1"
Asegúrate de hacer clic en Sync Now para sincronizar tu proyecto con los archivos Gradle modificados.
4. Agrega una configuración personalizada de WorkManager
En este paso, agregarás una configuración personalizada a la app a fin de modificar el nivel de registro de WorkManager para las compilaciones de depuración.
Paso 1: Inhabilita la inicialización predeterminada
Como se describe en la documentación de Configuración e inicialización personalizadas de WorkManager, debes inhabilitar la inicialización predeterminada en tu archivo AndroidManifest.xml
. Para ello, quita de la biblioteca de WorkManager el nodo que se combina automáticamente de forma predeterminada.
Para quitar este nodo, puedes agregar un nuevo nodo de proveedor a tu AndroidManifest.xml
, como se muestra a continuación:
AndroidManifest.xml
<application
...
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
</application>
También deberás agregar el espacio de nombres de las herramientas al manifiesto. El archivo completo con estos cambios será el siguiente:
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>
Paso 2: Agrega un Configuration.Provider a la clase Application
Para usar una inicialización a pedido, implementa la interfaz de Configuration.Provider
de WorkManager en tu clase Application
. La primera vez que tu app obtiene la instancia de WorkManager mediante getInstance(context)
, WorkManager se inicializa utilizando la configuración que muestra getWorkManagerConfiguration()
.
BlurApplication.kt
class BlurApplication : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.build()
...
}
Con este cambio, WorkManager se ejecuta con el registro establecido en DEBUG
.
Una opción más conveniente es configurar WorkManager de esta manera solo para las compilaciones de depuración de tu app, utilizando algo como lo siguiente:
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()
}
}
...
}
Una vez completo, BlurApplication.kt queda de la siguiente manera:
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())
}
}
}
Paso 3: Ejecuta la app en modo de depuración
Ahora, WorkManager está configurado para que tus compilaciones de depuración registren todos los mensajes provenientes de la biblioteca.
Cuando se ejecuta la app, puedes ver los registros en la pestaña logcat
de Android Studio:
Paso 4: ¿Qué puedes configurar?
La lista completa de parámetros se encuentra en la guía de referencia de WorkManager para Configuration.Builder
. Presta atención a dos parámetros adicionales:
WorkerFactory
- Rango de
JobId
Modificar WorkerFactory
te permite agregar otros parámetros al constructor de tu trabajador. Puedes encontrar más información sobre cómo implementar una instancia WorkerFactory personalizada en este artículo sobre cómo personalizar WorkManager. Si usas WorkManager y la API de JobScheduler
en tu app, te recomendamos personalizar el rango de JobId
para evitar que las dos API usen el mismo rango de JobId
.
Cómo compartir el progreso de WorkManager
WorkManager v2.3 agregó la funcionalidad para compartir información del progreso de tu trabajador a tu app mediante setProgressAsync()
(o setProgress()
cuando se utiliza desde CoroutineWorker
). Esta información se puede observar a través de WorkInfo, y está previsto que se la utilice para brindar comentarios al usuario en la IU. Luego, los datos de progreso se cancelan cuando el trabajador alcanza un estado final (SUCCEEDED, FAILED o CANCELLED). Para obtener más información sobre cómo publicar y observar el progreso, consulta Cómo observar el progreso intermedio de un trabajador.
Ahora, agregarás una barra de progreso en la IU para que, si la app se encuentra en primer plano, el usuario pueda ver cómo avanza el desenfoque. El resultado final será el siguiente:
Paso 1: Modifica ProgressBar
Para modificar la ProgressBar en el diseño, debes borrar el parámetro android:indeterminate="true"
, agregar el estilo style="@android:style/Widget.ProgressBar.Horizontal",
y establecer un valor inicial con android:progress="0"
. También debes establecer la orientación de LinearLayout
en "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>
También debes asegurarte de que la ProgressBar
se reinicie en la posición inicial. Para ello, actualiza la función showWorkFinished()
en el archivo 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
}
}
Paso 2: Observa la información de progreso en ViewModel
Ya existe un observador en el archivo BlurViewModel
que comprueba si se completó tu cadena. Agrega uno nuevo que observe el progreso publicado por BlurWorker
.
Primero, agrega algunas constantes para hacer un seguimiento de esto al final del archivo Constants.kt
:
app/src/main/java/com/example/background/Constants.kt
// Progress Data Key
const val PROGRESS = "PROGRESS"
const val TAG_PROGRESS = "TAG_PROGRESS"
El siguiente paso es agregar esta etiqueta a la WorkRequest
de BlurWorker
en el archivo BlurViewModel.kt
para que puedas recuperar su WorkInfo
. Desde ese WorkInfo
, puedes recuperar la información de progreso del trabajador:
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())
}
Agrega un nuevo LiveData
al archivo BlurViewModel.kt
que haga un seguimiento de esta WorkRequest
, e inicializa los LiveData
en el bloque 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
}
...
}
Paso 3: Observa LiveData en la actividad
Ahora puedes usar estos LiveData
en la BlurActivity
para observar todo el progreso publicado. Primero, registra un nuevo observador de LiveData
al final del 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())
Ahora puedes verificar la WorkInfo
que se recibió en el observador para ver si hay información de progreso y actualizar la ProgressBar
según corresponda:
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
}
}
}
}
Paso 4: Publica el progreso desde BullWorker
Todas las piezas necesarias para mostrar la información de progreso ya están en su lugar. Es hora de agregar la publicación real de la información de progreso a BlurWorker
.
En este ejemplo, simplemente se simula un proceso largo en nuestra función doWork()
a fin de publicar información sobre el progreso durante un período definido.
El cambio aquí consiste en intercambiar una única demora por 10 más pequeñas y establecer un progreso nuevo en cada iteración:
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()
}
...
}
Debido a que la demora original fue de 3 segundos, quizás te convenga reducirla por un factor de diez a 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
Paso 5: Ejecuta
Cuando se ejecuta la aplicación en este momento, debería mostrarse la ProgressBar propagada con los mensajes provenientes de BlurWorker
.
5. Prueba WorkManager
Las pruebas son un componente importante de cada aplicación y, al agregar una biblioteca como WorkManager, es importante proporcionar las herramientas para probar tu código con facilidad.
Con WorkManager, también ofrecemos algunos asistentes que te permiten probar fácilmente a tus trabajadores. Para obtener más información sobre cómo crear pruebas para tus trabajadores, consulta la documentación de WorkManager sobre las pruebas.
En esta sección del codelab, presentaremos algunas pruebas para nuestras clases de Worker en las que se muestran algunos de los casos de uso más comunes.
Primero, queremos brindarte una manera sencilla de configurar nuestras pruebas. Para ello, podemos crear una TestRule que configure WorkManager:
- Agregar dependencias
- Crear
WorkManagerTestRule
yTestUtils
- Crear prueba para
CleanupWorker
- Crear prueba para
BlurWorker
Suponiendo que ya hayas creado la carpeta AndroidTest en tu proyecto, necesitamos agregar algunas dependencias para usar en nuestras pruebas:
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"
Ahora podemos comenzar a reunir las piezas con una TestRule que podemos usar en nuestras pruebas:
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)
}
}
Debido a que necesitaremos esta imagen de prueba en el dispositivo (en el que se ejecutarán las pruebas), podemos crear algunas funciones auxiliares para usarlas en nuestras pruebas:
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
}
Una vez finalizado este trabajo, podemos comenzar a escribir nuestras pruebas.
Primero, probamos nuestro CleanupWorker
, para verificar que realmente borre nuestros archivos. Para ello, durante la prueba, copia la imagen de prueba en el dispositivo y, luego, verifica que esté allí una vez que se haya ejecutado el 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))
}
}
Ahora puedes ejecutar esta prueba en Android Studio desde el menú de ejecución o mediante el rectángulo verde a la izquierda de tu clase de prueba:
También puedes ejecutar tus pruebas desde la línea de comandos utilizando el comando ./gradlew cAT
de la carpeta raíz de tu proyecto.
Verás que tus pruebas se ejecutan correctamente.
Luego, podemos probar nuestro BlurWorker. Este trabajador espera datos de entrada con el URI de la imagen para procesar. Por lo tanto, podemos crear algunas pruebas: una que verifique que el trabajador falla si no hay un URI de entrada y otra que efectivamente procese la imagen 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))
}
}
Si ejecutas estas pruebas, ambas deberían completarse correctamente.
6. Felicitaciones
¡Felicitaciones! Terminaste la app de Blur-O-Matic y, en el proceso, aprendiste lo siguiente:
- Cómo crear una configuración personalizada
- Cómo publicar el progreso de tu trabajador
- Cómo mostrar el progreso del trabajo en la IU
- Cómo escribir pruebas para tus trabajadores
¡Buen "trabajo"! Para ver el estado final del código y todos los cambios, consulta lo siguiente:
Como alternativa, puedes clonar el codelab de WorkManager desde GitHub:
$ git clone -b advanced https://github.com/googlecodelabs/android-workmanager
WorkManager admite mucho más de lo que podríamos abarcar en este codelab. Para obtener más información, consulta la documentación de WorkManager.