Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does WorkManager schedule GET requests to REST API?

Tags:

I've had a look at the codelab for WorkManager plus some examples on here, but everything in code I have seen is either related to doing work locally on the device or work uploading to the server, not downloading data and responding to the data received. In the developer guidelines it even says, "For example, an app might need to download new resources from the network from time to time," so I thought it would be perfect for this task. My question is if WorkManager can handle the following scenario and if not, what is the proper tool for handling it:

  1. Schedule a job that runs once a day in background
  2. The job is to do a data fetch from the REST API (and post it to a LiveData object if possible).
  3. When the data returns, check that it is newer than local data.
  4. Notify the user that new data is available.

My worker class looks something like this:

public class MyWorker extends Worker {  @NonNull @Override public WorkerResult doWork() {     lookForNewData();     return WorkerResult.SUCCESS; }  public void lookForNewData() {     MutableLiveData<MyObject> liveData = new MutableLiveData<>();      liveData.observe(lifeCycleOwner, results -> {         notifyOnNewData(results);     })      APILayer.getInstance().fetchData(searchParams, liveData) } 

My issue is of course that the LiveData object can't observe because there is no activity or fragment that can be its LifecycleOwner. But even if I used a callback from the API to respond to the data arriving, my worker would already have posted that it was successful and it probably would not proceed with the callback, right? So I kind of know this approach is totally wrong, but I can't see any code for getting data with WorkManager

Please help with a proper solution and some example code or some links, either with WorkManager if it can handle this kind of work or something else if it is more appropriate.

like image 678
Arno Schoonbee Avatar asked Jun 08 '18 13:06

Arno Schoonbee


People also ask

How does a WorkManager work?

Android WorkManager is a background processing library which is used to execute background tasks which should run in a guaranteed way but not necessarily immediately. With WorkManager we can enqueue our background processing even when the app is not running and the device is rebooted for some reason.

What is difference between WorkManager and service?

WorkManager provides a better way of handling background tasks. Runs on devices with/without Google play services. No need to worry about threading as WorkManager will ensure execution on the background thread. Easy to schedule, cancel, retry & query the work.

What is difference between JobScheduler and WorkManager in Android?

As mentioned in the previous section, WorkManager internally uses the JobScheduler API on Android 6.0 (API Level 23 – Marshmallow) and above devices. Therefore, there won't be any major differences between the WorkManager and JobScheduler on Android 6.0 and above devices. Both would behave in the same way.

Does Work Manager work when app is closed?

WorkManager is intended for work that is required to run reliably even if the user navigates off a screen, the app exits, or the device restarts.


1 Answers

  1. Schedule a job that runs once a day in background

You can schedule a PeriodicWorkRequest for that, which should be queued with enqueueUniquePeriodicWork. This makes sure only one PeriodicWorkRequest of a particular name can be active at a time.

Constraints constraint = new Constraints.Builder()      .setRequiredNetworkType(NetworkType.CONNECTED)      .build();  PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(MyWorker.class, 1, TimeUnit.DAYS)      .setConstraints(constraint)      .build();  WorkManager workManager = WorkManager.getInstance(); workManager.enqueueUniquePeriodicWork("my_unique_worker", ExistingPeriodicWorkPolicy.KEEP, workRequest); 
  1. The job is to do a data fetch from the REST API (and post it to a LiveData object if possible).

This can by done by sending your request synchronously within doWork() of your worker. I wouldn't use LiveData within your Worker class. We come to that later. The API call would look with Retrofit for example like that:

@Override public WorkerResult doWork() {      Call<MyData> call = APILayer.getInstance().fetchData();      Response<MyData> response = call.execute();      if (response.code() == 200) {           MyData data = response.body();           // ...      } else {           return Result.RETRY;      }      // ...      return Result.SUCCESS; } 
  1. When the data returns, check that it is newer than local data.

You fetched your API data in a synchronous way. Fetch your local data also synchronously and do whatever you need to do to compare them.

  1. Notify the user that new data is available.

If you schedule a task with WorkManager it is guaranteed to run, even if your app is force-quit or the device is rebooted. So your task might complete while your app is not running. If you want to notify the user in any case you can send a notification. If you want to notify the user within a certain screen you can subscribe on your tasks status. For example like this (taken from the official guide):

WorkManager.getInstance().getStatusById(compressionWork.getId()) .observe(lifecycleOwner, workStatus -> {     // Do something with the status     if (workStatus != null && workStatus.getState().isFinished()) {         // ...     } }); 

There's also getStatusesForUniqueWork(String uniqueWorkName) for our example.

The official guide is also explaining how to return data from you Task with which you can call setValue() on your MutableLiveData for example.

I would propose to update your local data within your Worker, subscribe on your workers status and once it succeeds update your UI with the local data (if you are not subscribed on your local data anyways, i.e. with Room and LiveData).

Edit: In reference to point 4, reading status of periodic work requests works a little different. They are only switching between ENQUEUED and RUNNING until CANCELLED. But will never have the state SUCCEEDED or FAILED. So listening for isFinished() might not be what you are expecting.

like image 170
kphil Avatar answered Sep 23 '22 08:09

kphil