Thread in HearableWorker

In determinate situazioni, potrebbe essere necessario fornire una strategia di organizzazione in thread personalizzata. Ad esempio, potresti dover gestire un'operazione asincrona basata su callback. WorkManager supporta questo caso d'uso con ListenableWorker. ListenableWorker è l'API worker più di base; Worker, CoroutineWorker e RxWorker derivano tutte da questa classe. Un ListenableWorker segnala solo quando deve iniziare e finire il lavoro e lascia il thread a te. L'indicatore di inizio del lavoro viene richiamato sul thread principale, quindi è molto importante passare manualmente a un thread in background di tua scelta.

Il metodo astratto ListenableWorker.startWork() restituisce un ListenableFuture della Result. ListenableFuture è un'interfaccia leggera: si tratta di un Future che offre funzionalità per collegare i listener e propagare le eccezioni. Nel metodo startWork, devi restituire un ListenableFuture, che dovrai impostare con il Result dell'operazione una volta completata. Puoi creare istanze ListenableFuture in due modi:

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

Se volessi eseguire alcune operazioni basate su un callback asincrono, dovresti procedere 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;
        });
    }
}

Che cosa succede se il tuo lavoro viene interrotto? ListenableFuture di ListenableWorker viene sempre annullato quando si prevede l'interruzione del lavoro. Se utilizzi un elemento CallbackToFutureAdapter, devi semplicemente aggiungere un ascoltatore di annullamento, come indicato di seguito:

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 AscoltaableWorker 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 da te forniti 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 processo specifico:

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 anche aggiungere una definizione di servizio nel 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>

Samples