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 indica cuándo debe comenzar y detenerse el trabajo, y te deja la tarea completamente a ti. 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 establecerás con el Result
de la operación una vez que se haya completado. Puedes crear instancias de ListenableFuture
de una de las siguientes maneras:
- Si usas Guava, utiliza
ListeningExecutorService
. - De lo contrario, incluye
councurrent-futures
en tu archivo de Gradle y usaCallbackToFutureAdapter
.
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; }); } }
Cómo ejecutar un ListenableWorker en un proceso diferente
También puedes vincular un trabajador a un proceso específico mediante RemoteListenableWorker
, que es una implementación de ListenableWorker
.
RemoteListenableWorker
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(ExampleRemoteListenableWorker::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(ExampleRemoteListenableWorker.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>