Podział na wątki w ListenableWorker

W niektórych sytuacjach konieczne może być udostępnienie strategii podziału na wątki niestandardowe. Dla: może na przykład wymagać obsługi operacji asynchronicznej opartych na wywołaniu zwrotnym. WorkManager obsługuje ten przypadek użycia za pomocą funkcji ListenableWorker ListenableWorker to najbardziej podstawowy interfejs API instancji roboczej. Worker, CoroutineWorker oraz RxWorker – wszystkie z tych zajęć. O Funkcja ListenableWorker tylko sygnalizuje, kiedy zadanie ma się rozpocząć, zatrzymać i wyjść i układania wątków. Sygnał rozpoczęcia pracy jest wywoływany na głównym tak ważne jest przejście do wątku w tle możesz wybrać ręcznie.

Metoda abstrakcyjna ListenableWorker.startWork() zwraca ListenableFuture z Result O ListenableFuture to uproszczony interfejs. To Future, który zapewnia do dołączania detektorów i propagowania wyjątków. W startWork, powinien zostać zwrócony ListenableFuture, którego zostanie ustawiony na wartość Result operacji po jej zakończeniu. Możesz utworzyć ListenableFuture instancji na jeden z dwóch sposobów:

  1. Jeśli używasz Guawy, użyj ListeningExecutorService.
  2. W przeciwnym razie dołącz councurrent-futures w pliku Gradle i użyj CallbackToFutureAdapter

Jeśli chcesz wykonać pracę w oparciu o asynchroniczne wywołanie zwrotne, wykonaj zrób coś takiego:

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

Co się stanie, jeśli Twoja praca zatrzymano? Zadanie ListenableFuture dla ListenableWorker jest zawsze anulowane, gdy zadanie jest przestanie działać. Korzystając z CallbackToFutureAdapter, wystarczy dodać detektor anulowania w ten sposób:

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

Uruchamianie elementu ListenableWorker w innym procesie

Możesz też powiązać instancję roboczą z konkretnym procesem za pomocą polecenia RemoteListenableWorker implementacją ListenableWorker.

Funkcja RemoteListenableWorker wiąże się z określonym procesem za pomocą 2 dodatkowych argumentów którą podajesz jako część danych wejściowych podczas tworzenia zlecenia: ARGUMENT_CLASS_NAME i ARGUMENT_PACKAGE_NAME.

Poniższy przykład pokazuje tworzenie żądania pracy, które jest powiązane z konkretny proces:

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

W przypadku każdego elementu (RemoteWorkerService) musisz też dodać definicję usługi w: Twój plik 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>

Próbki