Thread in HearableWorker

In alcune situazioni, potrebbe essere necessario fornire una strategia di thread personalizzata. Per Ad esempio, potresti dover gestire un'operazione asincrona basata su callback. WorkManager supporta questo caso d'uso con ListenableWorker ListenableWorker è l'API worker di base; Worker, CoroutineWorker e RxWorker tutte derivano da questa classe. R ListenableWorker segnala solo quando il lavoro deve iniziare, terminare e se ne va il thread a te. L'indicatore di avvio del lavoro viene richiamato nell'istanza principale quindi è molto importante andare in un thread di background della manualmente.

Il metodo astratto ListenableWorker.startWork() restituisce un ListenableFuture del Result. R ListenableFuture è un'interfaccia leggera: è un Future che fornisce per collegare i listener e propagare le eccezioni. Nella startWork, dovresti restituire un ListenableFuture, che verrà impostato con Result dell'operazione una volta completata. Puoi creare ListenableFuture di istanze in uno dei due modi seguenti:

  1. Se usi Guava, usa ListeningExecutorService.
  2. In caso contrario, includi councurrent-futures nel file Gradle e utilizzare CallbackToFutureAdapter.

Se volessi eseguire alcune operazioni in base a un callback asincrono, dovresti procedi in questo modo:

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;
        });
    }
}

Cosa succede se il tuo lavoro è interrotto? Il ListenableFuture di ListenableWorker viene sempre annullato quando il lavoro viene è previsto l'arresto. Se usi un CallbackToFutureAdapter, devi solo aggiungere listener di annullamento, come segue:

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;
        });
    }
}

Esecuzione di ListenableWorker in un processo diverso

Puoi anche associare un worker a un processo specifico utilizzando RemoteListenableWorker, un'implementazione di ListenableWorker.

RemoteListenableWorker si associa a un processo specifico con due argomenti aggiuntivi che fornisci come parte dei dati di input durante la creazione della richiesta di lavoro: ARGUMENT_CLASS_NAME e ARGUMENT_PACKAGE_NAME.

L'esempio seguente mostra la creazione di una richiesta di lavoro associata a un una procedura specifica:

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();

Per ogni RemoteWorkerService, devi aggiungere anche una definizione di servizio in il tuo file 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>

Campioni