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:
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.
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.
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.
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.
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.
- 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);
- 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; }
- 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.
- 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With