In certain situations, you may need to provide a custom threading strategy. For example, you may need to handle a callback-based asynchronous operation. In this case, you cannot simply rely on a Worker
because it can't do the work in a blocking fashion. WorkManager supports this use case with ListenableWorker
. ListenableWorker
is the lowest-level worker API; Worker
, CoroutineWorker
, and RxWorker
all derive from this class. A ListenableWorker
only signals when the work should start and stop and leaves the threading entirely up to you. The start work signal is invoked on the main thread, so it is very important that you go to a background thread of your choice manually.
The abstract method ListenableWorker.startWork()
returns a ListenableFuture
of the Result
. A ListenableFuture
is a lightweight interface: it is a Future
that provides functionality for attaching listeners and propagating exceptions. In the startWork
method, you are expected to return a ListenableFuture
, which you will set with the Result
of the operation once it's completed. You can create ListenableFuture
s one of two ways:
- If you use Guava, use
ListeningExecutorService
. - Otherwise, include
councurrent-futures
in your gradle file and useCallbackToFutureAdapter
.
If you wanted to execute some work based on an asynchronous callback, you would do something like this:
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.google.com", callback);
}
return callback;
});
}
}
What happens if your work is stopped? A ListenableWorker
's ListenableFuture
is always cancelled when the work is expected to stop. Using a CallbackToFutureAdapter
, you simply have to add a cancellation listener, as follows:
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.google.com", callback);
}
return callback;
});
}
}