Exécuter des threads dans ListenableWorker

Dans certains cas, vous devrez peut-être fournir une stratégie de thread personnalisée. Pour exemple, vous devrez peut-être gérer une opération asynchrone basée sur le rappel. WorkManager prend en charge ce cas d'utilisation avec ListenableWorker ListenableWorker est l'API de nœud de calcul la plus basique ; Worker, CoroutineWorker RxWorker sont tous dérivés de cette classe. A ListenableWorker ne signale que le début et la fin de la tâche, et quand elle doit partir. entièrement à vous. Le signal de début des tâches est appelé Il est donc très important que vous accédiez à un thread d'arrière-plan manuellement.

La méthode abstraite ListenableWorker.startWork() renvoie un ListenableFuture des Result A 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 startWork, vous devez renvoyer un ListenableFuture, que vous est défini avec le Result de l'opération une fois celle-ci terminée. Vous pouvez créer ListenableFuture de l'une des deux manières suivantes:

  1. Si vous employez Guava, utilisez ListeningExecutorService.
  2. Sinon, incluez councurrent-futures dans votre fichier Gradle et utilisez CallbackToFutureAdapter

Si vous souhaitez exécuter une tâche basée sur un rappel asynchrone, vous devez faites quelque chose comme ceci:

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 si votre travail est arrêté ? Le ListenableFuture d'un ListenableWorker est toujours annulé lorsque la tâche est s'arrêtera. Avec un CallbackToFutureAdapter, il vous suffit d'ajouter un 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>

Exemples