Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Cómo ejecutar subprocesos en ListenableWorker

En ciertas situaciones, es posible que debas proporcionar una estrategia de subprocesos personalizada. Por ejemplo, es posible que debas controlar una operación asíncrona basada en devolución de llamada. En este caso, no puedes simplemente confiar en un objeto Worker, ya que no puede hacer el trabajo de forma bloqueada. WorkManager admite este caso práctico con ListenableWorker. ListenableWorker es la API de trabajador más básica; Worker, CoroutineWorker y RxWorker derivan de esta clase. Un ListenableWorker solamente señala cuándo debe comenzar y detenerse el trabajo, y te deja encargarte por completo del subproceso. Se invoca la señal de inicio de trabajo en el subproceso principal, por lo que es muy importante que accedas manualmente a un subproceso en segundo plano.

El método abstracto ListenableWorker.startWork() muestra un ListenableFuture del Result. Un ListenableFuture es una interfaz liviana: un objeto Future que proporciona funcionalidad para adjuntar detectores y propagar excepciones. En el método startWork, se espera que muestres un ListenableFuture, que deberás establecer con el Result de la operación una vez que se haya completado. Puedes crear instancias de ListenableFuture de una de las siguientes maneras:

  1. Si usas Guava, utiliza ListeningExecutorService.
  2. De lo contrario, incluye councurrent-futures en tu archivo de Gradle y usa CallbackToFutureAdapter.

Si quisieras ejecutar un trabajo basado en una devolución de llamada asíncrona, harías algo así:

Kotlin

class CallbackWorker(
        context: Context,
        params: WorkerParameters
) : ListenableWorker(context, params) {
    override fun startWork(): ListenableFuture<Result> {
        return CallbackToFutureAdapter.getFuture { completer ->
            val callback = object : Callback {
                var successes = 0

                override fun onFailure(call: Call, e: IOException) {
                    completer.setException(e)
                }

                override fun onResponse(call: Call, response: Response) {
                    successes++
                    if (successes == 100) {
                        completer.set(Result.success())
                    }
                }
            }

            repeat(100) {
                downloadAsynchronously("https://example.com", callback)
            }

            callback
        }
    }
}

Java

public class CallbackWorker extends ListenableWorker {

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

    @NonNull
    @Override
    public ListenableFuture<Result> startWork() {
        return CallbackToFutureAdapter.getFuture(completer -> {
            Callback callback = new Callback() {
                int successes = 0;

                @Override
                public void onFailure(Call call, IOException e) {
                    completer.setException(e);
                }

                @Override
                public void onResponse(Call call, Response response) {
                    successes++;
                    if (successes == 100) {
                        completer.set(Result.success());
                    }
                }
            };

            for (int i = 0; i < 100; i++) {
                downloadAsynchronously("https://www.example.com", callback);
            }
            return callback;
        });
    }
}

¿Qué pasa si tu trabajo se detiene? Un elemento ListenableWorker de ListenableFuture siempre se cancela cuando se espera que se detenga el trabajo. Con un CallbackToFutureAdapter, solo tienes que agregar un objeto de escucha de cancelación, de la siguiente manera:

Kotlin

class CallbackWorker(
        context: Context,
        params: WorkerParameters
) : ListenableWorker(context, params) {
    override fun startWork(): ListenableFuture<Result> {
        return CallbackToFutureAdapter.getFuture { completer ->
            val callback = object : Callback {
                var successes = 0

                override fun onFailure(call: Call, e: IOException) {
                    completer.setException(e)
                }

                override fun onResponse(call: Call, response: Response) {
                    ++successes
                    if (successes == 100) {
                        completer.set(Result.success())
                    }
                }
            }

 completer.addCancellationListener(cancelDownloadsRunnable, executor)

            repeat(100) {
                downloadAsynchronously("https://example.com", callback)
            }

            callback
        }
    }
}

Java

public class CallbackWorker extends ListenableWorker {

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

    @NonNull
    @Override
    public ListenableFuture<Result> startWork() {
        return CallbackToFutureAdapter.getFuture(completer -> {
            Callback callback = new Callback() {
                int successes = 0;

                @Override
                public void onFailure(Call call, IOException e) {
                    completer.setException(e);
                }

                @Override
                public void onResponse(Call call, Response response) {
                    ++successes;
                    if (successes == 100) {
                        completer.set(Result.success());
                    }
                }
            };

            completer.addCancellationListener(cancelDownloadsRunnable, executor);

            for (int i = 0; i < 100; ++i) {
                downloadAsynchronously("https://www.example.com", callback);
            }
            return callback;
        });
    }
}