Асинхронный работник в Android WorkManager

Google недавно анонсировала новый компонент архитектуры WorkManager. Это позволяет легко планировать синхронную работу, реализуя doWork() на Worker класс, а что если я хочу сделать некоторую асинхронную работу в фоновом режиме? Например, я хочу сделать вызов сетевой службы с помощью Retrofit. Я знаю, что могу сделать синхронный сетевой запрос, но он заблокирует поток и просто чувствует себя неправильно. Есть ли какое-либо решение или оно просто не поддерживается на данный момент?

4 ответов


Per документы WorkManager:

по умолчанию WorkManager выполняет свои операции в фоновом потоке. Если вы уже работаете в фоновом потоке и вам нужны синхронные (блокирующие) вызовы WorkManager, используйте synchronous() для доступа к таким методам.

поэтому, если вы не используете synchronous(), вы можете безопасно выполнять синхронизацию сетевых вызовов из doWork(). Это также лучший подход с точки зрения дизайна, потому что обратные вызовы грязный.

тем не менее, если вы действительно хотите уволить асинхронных заданий из doWork(), вам необходимо приостановить поток и возобновить его после завершения асинхронной работы с помощью wait/notify механизм (или какой-либо другой механизм управления потоками, например Semaphore). Не то, что я бы рекомендовал в большинстве случаев.

в качестве примечания, WorkManager находится в очень ранней альфа.


я использовал countdownlatch и ждал, пока это достигнет 0, что произойдет только после того, как асинхронный обратный вызов обновит его. См. этот код:

public WorkerResult doWork() {

        final WorkerResult[] result = {WorkerResult.RETRY};
        CountDownLatch countDownLatch = new CountDownLatch(1);
        FirebaseFirestore db = FirebaseFirestore.getInstance();

        db.collection("collection").whereEqualTo("this","that").get().addOnCompleteListener(task -> {
            if(task.isSuccessful()) {
                task.getResult().getDocuments().get(0).getReference().update("field", "value")
                        .addOnCompleteListener(task2 -> {
                            if (task2.isSuccessful()) {
                                result[0] = WorkerResult.SUCCESS;
                            } else {
                                result[0] = WorkerResult.RETRY;
                            }
                            countDownLatch.countDown();
                        });
            } else {
                result[0] = WorkerResult.RETRY;
                countDownLatch.countDown();
            }
        });

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return result[0];

    }

если вы говорите об асинхронной работе, вы можете переместить свою работу в RxJava Observables / Singles.

существует набор операторов, как .blockingGet() или .blockingFirst() что трансформирует Observable<T>, чтобы T

Worker выполняет в фоновом потоке, поэтому не беспокойтесь о NetworkOnMainThreadException.


я использовал BlockingQueue, что упрощает синхронизацию потоков и передача результата, вам понадобится только один объект

private var disposable = Disposables.disposed()


override fun doWork(): Result {
    val result = LinkedBlockingQueue<Result>()

    disposable = completable.subscribe(
            { result.put(Result.SUCCESS) },
            { result.put(Result.RETRY) }
    )

    return try {
        result.take()
    } catch (e: InterruptedException) {
        Result.RETRY
    }
}

также не забудьте освободить ресурсы, если ваш работник был остановлен, это главное преимущество над .blockingGet() как теперь вы можете правильно бесплатно отменить свою задачу Rx.

override fun onStopped(cancelled: Boolean) {
    disposable.dispose()
}