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