Cómo ejecutar subprocesos en CoroutineWorker

Para los usuarios de Kotlin, WorkManager proporciona compatibilidad de primera clase con corrutinas. Para comenzar, incluye work-runtime-ktx en tu archivo de Gradle. En lugar de extender Worker, deberías extender CoroutineWorker, que tiene una versión con suspensión de doWork(). Por ejemplo, si quieres compilar un CoroutineWorker simple para realizar algunas operaciones de red, debes hacer lo siguiente:

class CoroutineDownloadWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        val data = downloadSynchronously("https://www.google.com")
        saveData(data)
        return Result.success()
    }
}

Ten en cuenta que CoroutineWorker.doWork() es una función de suspensión. A diferencia de Worker, este código no se ejecuta en el Executor especificado en tu Configuration. En cambio, la opción predeterminada es Dispatchers.Default. Puedes personalizarlo si proporcionas tu propio CoroutineContext. En el ejemplo anterior, es probable que desees realizar el siguiente trabajo en Dispatchers.IO:

class CoroutineDownloadWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        withContext(Dispatchers.IO) {
            val data = downloadSynchronously("https://www.google.com")
            saveData(data)
            return Result.success()
        }
    }
}

CoroutineWorker controla las detenciones automáticamente mediante la cancelación de la corrutina y la propagación de los indicadores de cancelación. No es necesario que hagas nada especial para controlar las interrupciones de trabajo.

Cómo ejecutar un CoroutineWorker en un proceso diferente

También puedes vincular un trabajador a un proceso específico mediante RemoteCoroutineWorker, que es una implementación de ListenableWorker.

RemoteCoroutineWorker vincula un proceso específico con dos argumentos adicionales que puedes proporcionar como parte de los datos de entrada cuando compilas la solicitud de trabajo: ARGUMENT_CLASS_NAME y ARGUMENT_PACKAGE_NAME.

En el siguiente ejemplo, se muestra cómo compilar una solicitud de trabajo vinculada a un proceso específico:

Kotlin

val PACKAGE_NAME = "com.example.background.multiprocess"

val serviceName = RemoteWorkerService::class.java.name
val componentName = ComponentName(PACKAGE_NAME, serviceName)

val data: Data = Data.Builder()
   .putString(ARGUMENT_PACKAGE_NAME, componentName.packageName)
   .putString(ARGUMENT_CLASS_NAME, componentName.className)
   .build()

return OneTimeWorkRequest.Builder(ExampleRemoteCoroutineWorker::class.java)
   .setInputData(data)
   .build()

Java

String PACKAGE_NAME = "com.example.background.multiprocess";

String serviceName = RemoteWorkerService.class.getName();
ComponentName componentName = new ComponentName(PACKAGE_NAME, serviceName);

Data data = new Data.Builder()
        .putString(ARGUMENT_PACKAGE_NAME, componentName.getPackageName())
        .putString(ARGUMENT_CLASS_NAME, componentName.getClassName())
        .build();

return new OneTimeWorkRequest.Builder(ExampleRemoteCoroutineWorker.class)
        .setInputData(data)
        .build();

Para cada RemoteWorkerService, también debes agregar una definición del servicio en el archivo AndroidManifest.xml:

<manifest ... >
    <service
            android:name="androidx.work.multiprocess.RemoteWorkerService"
            android:exported="false"
            android:process=":worker1" />

        <service
            android:name=".RemoteWorkerService2"
            android:exported="false"
            android:process=":worker2" />
    ...
</manifest>

Ejemplos