Cómo ejecutar subprocesos en Worker

Cuando usas un Worker, WorkManager llama automáticamente a Worker.doWork() en un subproceso, en segundo plano. El subproceso en segundo plano proviene del Executor que se especificó en Configuration de WorkManager. De forma predeterminada, WorkManager configura un Executor por ti, pero también puedes personalizar el tuyo. Por ejemplo, puedes compartir un ejecutor existente en segundo plano en la app, crear un Executor de un solo subproceso para asegurarte de que todas las tareas en segundo plano se ejecuten de forma secuencial, o incluso especificar un Executor personalizado. Para personalizar el Executor, asegúrate de iniciar WorkManager de forma manual.

Cuando configuras WorkManager manualmente, puedes especificar tu Executor de la siguiente manera:

Kotlin

WorkManager.initialize(
    context,
    Configuration.Builder()
         // Uses a fixed thread pool of size 8 threads.
        .setExecutor(Executors.newFixedThreadPool(8))
        .build())

Java

WorkManager.initialize(
    context,
    new Configuration.Builder()
        .setExecutor(Executors.newFixedThreadPool(8))
        .build());

A continuación, se muestra un ejemplo de un Worker simple que descarga el contenido de una página web 100 veces:

Kotlin

class DownloadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {

    override fun doWork(): ListenableWorker.Result {
        repeat(100) {
            try {
                downloadSynchronously("https://www.google.com")
            } catch (e: IOException) {
                return ListenableWorker.Result.failure()
            }
        }

        return ListenableWorker.Result.success()
    }
}

Java

public class DownloadWorker extends Worker {

    public DownloadWorker(Context context, WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public Result doWork() {
        for (int i = 0; i < 100; i++) {
            try {
                downloadSynchronously("https://www.google.com");
            } catch (IOException e) {
                return Result.failure();
            }
        }

        return Result.success();
    }

}

Ten en cuenta que Worker.doWork() es una llamada síncrona; se espera que hagas todas las tareas en segundo plano de forma bloqueada y que finalices cuando el método se cierre. Si llamas a una API asíncrona en doWork() y muestras un Result, es posible que la devolución de llamada no funcione correctamente. Si te encuentras con esta situación, considera usar un ListenableWorker (consulta Cómo ejecutar subprocesos en ListenableWorker).

Cuando un Worker que está en ejecución se detiene por algún motivo, recibe una llamada a Worker.onStopped(). Anula este método o llama a Worker.isStopped() para establecer un punto de control de tu código y liberar recursos según sea necesario. Cuando se detiene Worker en el ejemplo anterior, puede estar en el medio de su bucle de descarga de elementos y continuará haciéndolo aunque se haya detenido. Para optimizar este comportamiento, puedes hacer lo siguiente:

Kotlin

class DownloadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {

    override fun doWork(): ListenableWorker.Result {
        repeat(100) {
            if (isStopped) {
                break
            }

            try {
                downloadSynchronously("https://www.google.com")
            } catch (e: IOException) {
                return ListenableWorker.Result.failure()
            }

        }

        return ListenableWorker.Result.success()
    }
}

Java

public class DownloadWorker extends Worker {

    public DownloadWorker(Context context, WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public Result doWork() {
        for (int i = 0; i < 100; ++i) {
            if (isStopped()) {
                break;
            }

            try {
                downloadSynchronously("https://www.google.com");
            } catch (IOException e) {
                return Result.failure();
            }
        }

        return Result.success();
    }
}

Una vez que se detiene un Worker, no importa lo que muestres de Worker.doWork(); el Result se ignorará.