Synchronous or Asynchronous Rxjava inside the Worker (from WorkManager component) what's the right choice?
R

5

21

I'm new to the new architecture component WorkManager, I do my API calls via Retrofit and RxJava.

My use case here is to get new posts from the Backend, then show notification, and update a widget.

So the code inside doWork() method from the Worker class, may look like something like this.

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

My Question is what is the right way to use a RxJava code inside the Worker Class because the doWork() method has a return value, so Do I have to make Rx code Synchronous.

if I'm using the nonblocking Rx approach, how can I return value (Success - Failure - Retry)

Remnant answered 8/8, 2018 at 22:5 Comment(6)
Inside the lambda, we can obviously return any of the either statuses - Success, Failure or Retry only making sure that the same line is in the last line of execution.Wiggler
@Wiggler could you please make it more clear with some codeRemnant
use lambda with open braces inside onError or onSuccess and since we know either condition works and are probably the last one in chaining, inside braces do your work and in the last line, return the respective status to WorkManagerWiggler
'return Result.RETRY' at the end is okay and should reach only on rare cases. Mostly you would want to return the status from inside.Wiggler
BTW, obtaining a reference to the Application by casting the application context is not guaranteed to work. I've mostly seen it fail in emulators, but also very rarely on real devices. It's safer to set a static field in the application's onCreate and provide a static getter.Brachium
can you share the last result with us, I'm lost and I need some hintsChastitychasuble
V
38

Since WorkManager version 1.0.0-alpha12 they added a new artifact called work-rxjava2 that includes RxWorker class exactly for this purpose. It is a special case of ListenableWorker expecting Single<Result>.

To implement it, first make sure you include correct artifacts to your build.gradle:

dependencies {
   ...
   implementation "android.arch.work:work-runtime-ktx:$work_version"
   implementation "android.arch.work:work-rxjava2:$work_version"
}

And implement your 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() }
    }
}
Vern answered 16/12, 2018 at 17:56 Comment(2)
What the RemoteService is ? Can you explain with more details please ? Because I get stuck with this issue for couple daysAney
remote service is just some example service you might use to return some value wrapped in Single type.Vern
D
6

Edit: WorkManager now officially supports an RxWorker. Take a look at the answer above for more information.

doWork happens on a background thread. So it's safe to block. You should wait for the Observable to complete before you return a Result.

We are also working on making this easier with asynchronous APIs. Stay tuned.

Dishonorable answered 9/8, 2018 at 20:17 Comment(0)
B
1

Yes, make the Rx code synchronous. The documentation for doWork is minimal, but the description

Override this method to do your actual background processing.

implies that it's expected or at least allowed to block. And of course, you cannot know what doWork should return until the network request has been resolved.

Brachium answered 9/8, 2018 at 0:16 Comment(0)
A
0

I found the solution. You should use RxWorker or SettableFuture for async job

This is my solution for getting current location. Working like a charm

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
   }
}
Aney answered 26/2, 2019 at 4:38 Comment(0)
S
0

You can use both Rxjava and Coroutine with Work Manager. Have a look at this Medium post. Hopefully it will help you. Thank you.

Safeguard answered 18/2, 2021 at 12:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.