Синхронный или асинхронный Rxjava внутри Worker (из компонента WorkManager), какой правильный выбор?

Я новичок в новом компоненте архитектуры WorkManager, я выполняю свои API-вызовы через Retrofit и RxJava.

Моим вариантом использования здесь является получение новых сообщений из Backend, затем просмотр уведомлений и обновление виджета.

Таким образом, код внутри метода doWork() из класса Worker может выглядеть примерно так.

@NonNull
  @Override
  public Result doWork() {
    AppDependencies appDependencies = new AppDependencies((Application) getApplicationContext());
    Repository repository = appDependencies.getRepository();

    repository.getNewPosts()
        .flatMap(newPosts -> repository.inserPosts(newPosts).toObservable())
        .doOnError(Timber::e)
        //if success - > return  Result.SUCCESS,
        // -> show notification
        // -> update widget
        // error-> return Result.Failure
        .dontKnowWhatBestNextThing; //blocking or subscribing

    //if we reached here then Retry
    return Result.RETRY;
  }

Мой вопрос - это правильный способ использования кода RxJava внутри класса Worker, потому что метод doWork() имеет возвращаемое значение, поэтому мне нужно сделать Rx-код синхронным.

если я использую неблокирующий Rx-подход, как я могу вернуть значение (Success - Failure - Retry)

Ответ 1

Начиная с версии WorkManager 1.0.0-alpha12 они добавили новый артефакт под названием work-rxjava2 который включает класс RxWorker именно для этой цели. Это особый случай ListenableWorker ожидающего Single<Result>.

Чтобы реализовать это, сначала убедитесь, что вы включили правильные артефакты в свой build.gradle:

dependencies {
   ...
   implementation "android.arch.work:work-runtime-ktx:1.0.0-beta01"
   implementation "android.arch.work:work-rxjava2:1.0.0-beta01"
}

И RxWorker свой RxWorker:

class MyRxWorker(context : Context, params : WorkerParameters) : RxWorker(context, params) {

    val remoteService = RemoteService()

    override fun createWork(): Single<Result> {
        return remoteService.getMySingleResponse()
                .doOnSuccess { /* process result somehow */ }
                .map { Result.success() }
                .onErrorReturn { Result.failure() }
    }
}

Ответ 2

Изменить: WorkManager теперь официально поддерживает RxWorker. Посмотрите на ответ выше для получения дополнительной информации.

doWork происходит в фоновом потоке. Так что это безопасно заблокировать. Вам следует подождать, пока Observable завершится, прежде чем вернуть Result.

Мы также работаем над тем, чтобы сделать это проще с помощью асинхронных API. Оставайтесь в курсе.

Ответ 3

Да, сделать код Rx синхронным. Документация для doWork минимальна, но описание

Переопределите этот метод, чтобы выполнить фактическую фоновую обработку.

подразумевает, что он ожидал или, по крайней мере, позволил заблокировать. И, конечно, вы не можете знать, что должен вернуть doWork пока не будет решен сетевой запрос.

Ответ 4

Я нашел решение. Вы должны использовать RxWorker или SettableFuture для асинхронной работы

Это мое решение для получения текущего местоположения. Работая как шарм

class LocationWorker(context: Context, private val workerParams: WorkerParameters) :
ListenableWorker(context, workerParams) {

lateinit var mFuture: SettableFuture<ListenableWorker.Result>
private var fusedLocationProviderClient = FusedLocationProviderClient(context)

@SuppressLint("RestrictedApi", "MissingPermission")
override fun startWork(): ListenableFuture<Result> {
    val uniqueId = workerParams.inputData.getString(UNIQUE_ID_KEY)
    mFuture = SettableFuture.create()
    Timber.d("mFutureStart")
    fusedLocationProviderClient.lastLocation.addOnSuccessListener { location ->
        Timber.d("location == $location")
        if (location != null) {
            mFuture.set(Result.success())
        } else mFuture.set(Result.failure())
      }
    return mFuture
   }
}