1. Introduction
Cet atelier de programmation traite des concepts avancés de WorkManager. Il s'appuie sur les concepts de base traités dans l'atelier de programmation Travail en arrière-plan avec WorkManager.
Voici quelques autres ressources pour vous familiariser avec WorkManager :
- Guide WorkManager
- Série d'articles de blogs : Présentation de WorkManager
- Présentation de WorkManager ADS 2019 : WorkManager : fonctionnalités avancées
- WorkManager : série "Compétences en développement Android moderne"
Objectifs de l'atelier
Dans cet atelier de programmation, vous allez utiliser Blur-O-Matic, une application qui permet de flouter des photos et des images, et d'enregistrer le résultat dans un fichier. Si vous avez déjà terminé l'atelier de programmation Travail en arrière-plan avec WorkManager, vous allez retrouver ici un exemple d'application similaire. La seule différence est qu'ici vous pouvez sélectionner votre propre image dans la galerie pour la flouter. Dans cet atelier, vous allez ajouter des fonctionnalités au code :
- Configuration personnalisée
- Utilisation de l'API Progress pour mettre à jour l'interface utilisateur pendant que le travail s'exécute
- Test de vos nœuds de calcul
Prérequis
Pour cet atelier de programmation, vous devez disposer de la dernière version stable d'Android Studio.
Vous devez également connaître les classes LiveData
, ViewModel
et View Binding
. Si vous ne les connaissez pas encore, consultez l'atelier de programmation sur les composants du cycle de vie d'Android (en particulier pour ViewModel et LiveData) ou l'atelier de programmation sur Room (présentation des composants de l'architecture).
Si vous êtes bloqué
Si vous vous retrouvez bloqué au cours de cet atelier de programmation ou si vous souhaitez voir le code final, vous pouvez .
Ou, si vous préférez, vous pouvez cloner l'atelier de programmation WorkManager complet depuis GitHub :
$ git clone -b advanced https://github.com/googlecodelabs/android-workmanager
2. Configuration
Étape 1 : Téléchargez le code
Cliquez sur le lien suivant pour télécharger la version du code nécessaire pour suivre cet atelier de programmation :
Ou, si vous préférez, vous pouvez cloner l'atelier de programmation depuis GitHub :
$ git clone -b advanced_start https://github.com/googlecodelabs/android-workmanager
Étape 2 : Exécutez l'application
Exécutez l'application. Les écrans suivants devraient s'afficher. Veillez bien à autoriser l'application à accéder à vos photos lorsque vous y êtes invité.
Vous pouvez sélectionner une image et passer à l'écran suivant, qui contient des cases d'option pour sélectionner le niveau de flou de l'image. Appuyez sur le bouton Go (Appliquer) pour flouter et enregistrer l'image. Lors du floutage, l'application affiche le bouton Cancel (Annuler) pour que vous puissiez interrompre l'opération.
Le code de départ contient les éléments suivants :
WorkerUtils
: cette classe contient le code du floutage, ainsi que des méthodes pratiques que vous utiliserez plus tard pour afficherNotifications
et ralentir l'application.BlurApplication
: classe d'application avec une méthodeonCreate()
simple pour initialiser le système de journalisation Timber pour les versions de débogage.BlurActivity
: activité qui permet d'afficher l'image, et qui inclut des cases d'option pour sélectionner le niveau de flou.BlurViewModel
: ce modèle de vue stocke toutes les données nécessaires à l'affichage deBlurActivity
. C'est également dans cette classe que vous démarrez le travail en arrière-plan à l'aide de WorkManager.Workers/CleanupWorker
: ce nœud de calcul supprime toujours les fichiers temporaires existants.Workers/BlurWorker
: ce nœud de calcul floute l'image transmise en tant que données d'entrée avec un URI et renvoie l'URI du fichier temporaire.Workers/SaveImageToFileWorker
: ce nœud de calcul utilise l'URI de l'image temporaire et renvoie l'URI du fichier final.Constants
: classe statique comprenant quelques constantes que vous utiliserez dans cet atelier de programmation.SelectImageActivity
: première activité vous permettant de sélectionner une image.res/activity_blur.xml
etres/activity_select.xml
: fichiers de mise en page pour chaque activité.
Vous allez modifier le code dans les classes suivantes : BlurApplication
, BlurActivity
, BlurViewModel
et BlurWorker
.
3. Ajouter WorkManager à votre application
WorkManager
nécessite la dépendance Gradle ci-dessous. Ils ont déjà été inclus dans les fichiers :
app/build.gradle
dependencies {
implementation "androidx.work:work-runtime-ktx:$versions.work"
}
Vous devez télécharger la dernière version de work-runtime
depuis la page de téléchargement des versions de WorkManager et utiliser la dernière version stable, ou utiliser la version suivante :
build.gradle
versions.work = "2.7.1"
Veillez à cliquer sur Sync Now (Synchroniser maintenant) pour synchroniser votre projet avec les fichiers Gradle modifiés.
4. Ajouter une configuration personnalisée de WorkManager
Dans cette étape, vous allez ajouter à l'application une configuration personnalisée afin de modifier le niveau de journalisation de WorkManager pour les versions de débogage.
Étape 1 : Désactivez l'initialisation par défaut
Comme décrit dans la documentation Custom WorkManager configuration and initialization (Configuration personnalisée et initialisation de WorkManager), vous devez désactiver l'initialisation par défaut dans le fichier AndroidManifest.xml
en supprimant le nœud qui est automatiquement fusionné par défaut depuis la bibliothèque WorkManager.
Pour supprimer ce nœud, vous pouvez ajouter un nouveau nœud de fournisseur à AndroidManifest.xml
, comme indiqué ci-dessous :
AndroidManifest.xml
<application
...
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
</application>
Vous devez également ajouter l'espace de noms des outils au fichier manifeste. Le fichier complet contenant ces modifications est le suivant :
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>
Étape 2 : Ajoutez Configuration.Provider à la classe Application
Vous pouvez utiliser une initialisation à la demande en implémentant l'interface Configuration.Provider
de WorkManager dans la classe Application
. La première fois que votre application récupère l'instance WorkManager à l'aide de getInstance(context)
, WorkManager s'initialise en utilisant la configuration renvoyée par getWorkManagerConfiguration()
.
BlurApplication.kt
class BlurApplication : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.build()
...
}
Avec ce changement, WorkManager s'exécute avec la journalisation définie sur DEBUG
.
Il est préférable de configurer WorkManager de cette manière uniquement pour les versions de débogage de votre application, en utilisant par exemple :
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()
}
}
...
}
BlurApplication.kt devient alors :
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())
}
}
}
Étape 3 : Exécutez l'application en mode de débogage
WorkManager est à présent configuré de manière que vos versions de débogage consignent tous les messages provenant de la bibliothèque.
Si vous exécutez l'application, vous pouvez voir que les journaux s'affichent dans l'onglet logcat
d'Android Studio :
Étape 4 : Quels sont les paramètres configurables ?
La liste complète des paramètres est disponible dans le guide de référence de WorkManager pour Configuration.Builder
. Faites attention à deux paramètres supplémentaires :
WorkerFactory
- Plage
JobId
La modification de la valeur WorkerFactory
permet d'ajouter des paramètres au constructeur de votre nœud de calcul. Pour en savoir plus sur l'implémentation d'un WorkerFactory personnalisé, consultez l'article Customizing WorkManager (Personnaliser WorkManager). Si vous utilisez WorkManager ainsi que l'API JobScheduler
dans votre application, nous vous recommandons de personnaliser la plage JobId
pour éviter que la même plage JobId
soit utilisée par les deux API.
Partage de la progression de WorkManager
WorkManager v2.3 intègre une nouvelle fonctionnalité permettant de partager avec votre application les informations sur l'avancement du nœud de calcul à l'aide de setProgressAsync()
(ou de setProgress()
si vous partez d'un CoroutineWorker
). Ces informations sont accessibles via WorkInfo. Elles sont destinées à fournir à l'utilisateur des indications dans l'interface. Les données de progression sont ensuite annulées lorsque le nœud de calcul atteint un état final SUCCEEDED, FAILED ou CANCELLED (RÉUSSITE, ÉCHEC ou ANNULÉ). Pour en savoir plus sur la publication et l'écoute de la progression, consultez l'article Observing intermediate Worker progress (Observer la progression d'un nœud de calcul intermédiaire).
Vous allez maintenant ajouter une barre de progression dans l'interface afin que l'utilisateur puisse voir la progression du floutage lorsque l'application se trouve au premier plan. Le résultat final ressemble à ceci :
Étape 1 : Modifiez ProgressBar
Pour modifier ProgressBar dans la mise en page, vous devez supprimer le paramètre android:indeterminate="true"
, ajouter le style style="@android:style/Widget.ProgressBar.Horizontal",
et définir une valeur initiale avec android:progress="0"
Vous devez aussi définir l'orientation LinearLayout
sur "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>
L'autre modification nécessaire consiste à s'assurer que ProgressBar
redémarre à la position initiale. Pour ce faire, mettez à jour la fonction showWorkFinished()
dans le fichier 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
}
}
Étape 2 : Observez les informations sur la progression dans ViewModel
Le fichier BlurViewModel
comporte déjà un observateur qui vérifie quand votre chaîne est terminée. Ajoutez-en un autre pour observer la progression publiée par BlurWorker
.
Tout d'abord, ajoutez des constantes pour en effectuer le suivi à la fin du fichier Constants.kt
:
app/src/main/java/com/example/background/Constants.kt
// Progress Data Key
const val PROGRESS = "PROGRESS"
const val TAG_PROGRESS = "TAG_PROGRESS"
L'étape suivante consiste à ajouter ce tag au WorkRequest
de BlurWorker
dans le fichier BlurViewModel.kt
afin de pouvoir récupérer WorkInfo
. Depuis WorkInfo
, vous pouvez récupérer les informations sur la progression du nœud de calcul :
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())
}
Ajoutez un nouveau LiveData
au fichier BlurViewModel.kt
qui effectue le suivi de WorkRequest
, puis initialisez LiveData
dans le bloc 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
}
...
}
Étape 3 : Observez LiveData dans l'activité
Vous pouvez désormais utiliser LiveData
dans BlurActivity
pour observer l'intégralité de la progression publiée. Enregistrez tout d'abord un nouvel observateur LiveData
à la fin de la méthode 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())
Vous pouvez maintenant vérifier le WorkInfo
reçu dans l'observateur pour voir s'il existe des informations sur la progression, puis mettre à jour ProgressBar
en conséquence :
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
}
}
}
}
Étape 4 : Publiez la progression depuis BlurWorker
Tous les éléments nécessaires à l'affichage des informations sur la progression sont désormais en place. Il est temps d'ajouter la publication réelle des informations sur la progression dans BlurWorker
.
Cet exemple simule simplement un long processus dans notre fonction doWork()
afin de pouvoir publier des informations sur la progression sur une période définie.
La modification ici consiste à remplacer un seul retard par 10 petits retards, afin de définir une nouvelle progression à chaque itération :
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()
}
...
}
Comme le retard initial était de trois secondes, il est probablement judicieux de le réduire également d'un facteur 10, à 0,3 seconde :
app/src/main/java/com/example/background/Constants.kt
// const val DELAY_TIME_MILLIS: Long = 3000
const val DELAY_TIME_MILLIS: Long = 300
Étape 5 : Exécutez l'application
Si vous exécutez l'application à ce stade, la barre de progression devrait s'afficher avec les messages provenant de BlurWorker
.
5. Tester WorkManager
La phase de test est essentielle pour chaque application. Lorsque vous introduisez une bibliothèque telle que WorkManager, il est important de fournir les outils permettant de tester facilement votre code.
Avec WorkManager, nous avons aussi mis à disposition quelques assistants pour tester facilement vos nœuds de calcul. Pour en savoir plus sur la création de tests pour vos nœuds de calcul, consultez la documentation de WorkManager sur les tests.
Dans cette section de l'atelier de programmation, nous allons effectuer des tests pour les classes de nœud de calcul, pour quelques cas d'utilisation courants.
Tout d'abord, nous souhaitons fournir un moyen simple de configurer les tests. Pour ce faire, nous pouvons créer une règle TestRule qui configure WorkManager :
- Ajouter des dépendances
- Créer
WorkManagerTestRule
etTestUtils
- Créer un test pour
CleanupWorker
- Créer un test pour
BlurWorker
Nous supposons que vous avez déjà créé le dossier AndroidTest dans votre projet. Vous devez alors ajouter des dépendances à utiliser dans les tests :
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"
Nous pouvons maintenant commencer à assembler les pièces du puzzle avec une règle TestRule, que nous pouvons utiliser dans les tests :
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)
}
}
Puisque nous avons besoin de cette image de test sur l'appareil (où les tests seront exécutés), nous allons créer des fonctions d'assistance à utiliser dans les tests :
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
}
Une fois que c'est fait, nous pouvons commencer à écrire les tests.
Nous allons commencer par tester CleanupWorker
, afin de vérifier qu'il supprime bien nos fichiers. Pour ce faire, copiez l'image de test sur l'appareil dans le test, puis vérifiez si elle est là après que CleanupWorker
a été exécuté :
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))
}
}
Vous pouvez à présent exécuter ce test depuis le menu "Exécuter" d'Android Studio ou en utilisant le rectangle vert à gauche de votre classe de test :
Vous pouvez aussi exécuter vos tests depuis une ligne de commande, en utilisant la commande ./gradlew cAT
depuis le dossier racine de votre projet.
Les tests devraient s'exécuter correctement.
Nous pouvons ensuite tester BlurWorker. Ce nœud de calcul attend une donnée d'entrée avec l'URI de l'image à traiter. Nous pouvons donc créer deux tests : un qui vérifie que le nœud de calcul échoue en l'absence d'URI d'entrée et un autre qui traite l'image d'entrée.
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 vous exécutez ces tests, les deux devraient réussir.
6. Félicitations
Félicitations ! Vous venez de terminer l'application Blur-O-Matic. Vous savez maintenant :
- créer une configuration personnalisée ;
- publier la progression à partir du nœud de calcul ;
- afficher la progression du travail dans l'interface utilisateur ;
- écrire des tests pour vos nœuds de calcul.
Beau travail ! Pour voir l'état final du code et toutes les modifications, consultez la page :
Ou, si vous préférez, vous pouvez cloner l'atelier de programmation de WorkManager à partir de GitHub :
$ git clone -b advanced https://github.com/googlecodelabs/android-workmanager
Workmanager est compatible avec bien d'autres fonctionnalités que nous n'avons pu traiter dans cet atelier de programmation. Pour en savoir plus, consultez la documentation de WorkManager.