Dans certains cas, vous devrez peut-être fournir une stratégie de thread personnalisée. Par exemple, vous devrez peut-être gérer une opération asynchrone basée sur le rappel. Dans ce cas, vous ne pouvez pas simplement vous appuyer sur un Worker
, car il ne peut pas effectuer le travail de blocage. WorkManager accepte ce cas d'utilisation avec ListenableWorker
. ListenableWorker
est l'API de nœud de calcul la plus basique ; Worker
, CoroutineWorker
et RxWorker
sont tous dérivés de cette classe. Un ListenableWorker
indique uniquement quand le travail doit démarrer et s'arrêter, laissant les threads sous votre entière responsabilité. Le signal de début du travail est appelé sur le thread principal. Il est donc très important que vous accédiez manuellement au thread d'arrière-plan de votre choix.
La méthode abstraite ListenableWorker.startWork()
renvoie un ListenableFuture
du Result
. Un ListenableFuture
est une interface légère : il s'agit d'un Future
qui fournit une fonctionnalité permettant d'associer des écouteurs et de propager des exceptions. Dans la méthode startWork
, vous devez renvoyer ListenableFuture
, que vous définirez avec le Result
de l'opération une fois celle-ci terminée. Vous pouvez créer des instances ListenableFuture
de deux manières :
- Si vous employez Guava, utilisez
ListeningExecutorService
. - Sinon, incluez
councurrent-futures
dans votre fichier Gradle et utilisezCallbackToFutureAdapter
.
Si vous souhaitez exécuter une tâche basée sur un rappel asynchrone, procédez comme suit :
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; }); } }
Que se passe-t-il en cas d'arrêt de votre tâche ? Le ListenableFuture
d'un ListenableWorker
est toujours annulé lorsque la tâche doit s'arrêter. Avec CallbackToFutureAdapter
, il vous suffit d'ajouter un écouteur d'annulation comme suit :
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; }); } }
Exécuter un ListenableWorker dans un processus différent
Vous pouvez également lier un nœud de calcul à un processus spécifique à l'aide de RemoteListenableWorker
, une implémentation de ListenableWorker
.
RemoteListenableWorker
se lie à un processus spécifique avec deux arguments supplémentaires que vous fournissez dans les données d'entrée lors de la création de la requête de tâche : ARGUMENT_CLASS_NAME
et ARGUMENT_PACKAGE_NAME
.
L'exemple suivant illustre la création d'une requête de tâche liée à un processus spécifique :
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();
Pour chaque RemoteWorkerService
, vous devez également ajouter une définition de service dans votre fichier 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>