How to implement PeriodicWorkRequest chain? - android

I study Android WorkManager, and fond one problem.
I have 2 Works, first of them fetch some data from server and second preload resources (depends on result of first work). I need doing this chains one time per hour.
I need something like:
workManager.beginWith(work1).then(work2)
But in WorkManger API I found chain only for OneTimeWorkRequest.

You cannot chain PeriodicWorkRequests. For your use-case you might consider using a OneTimeWorkRequest with a Worker that enqueues a copy of itself at the end of doWork() with an initial delay (to simulate periodicity).
That way you can still do chaining. I would tag all work requests consistently so you can getWorkInfosByTagLiveData() correctly.

Promoting Andrew's comment into an answer:
Google's had an open ticket to add this functionality
(see here).
Google's official solution there was to create a periodic work request with an "chain initiation" worker, in that worker's doWork() define and enqueue a chain of one-time work requests.

I was able do to it through creating a simple PeriodicWorkRequest with its doWork() implements the following
override suspend fun doWork(): Result {
...
WorkManager.getInstance(context).beginWith(oneTimeWork1).then(oneTimeWork2).enqueue()
Result.success()
}
Not ideal but this way it triggers the chain at its periodic interval

Related

What is most efficient way schedule work using WorkManager for a system level event- using addContentUriTrigger in Constraints or BroadcastReceiver?

I am having a use case where I need to register an incoming call in through my app and later use that data for something. One thing that is clear to me is that we need a WorkManager to run in the background to perform this task, however, the confusion lies in the implementation of this. The logical approach is that the WorkManager's doWork() should be triggered when the call event occurs.
Now, there can be two different implementations for this-
Using a BroadcastReceiver (to register the call event) and then enqueue the workRequest in the onReceive() of the BroadcastReceiver
Adding the Constraints in the workRequest in the following way:
//some code here
val constraints = Constraints.Builder()
.addContentUriTrigger(CallLog.Calls.CONTENT_URI, true)
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val workRequest = OneTimeWorkRequestBuilder<JobWorker>().apply {
setConstraints(constraints)
setInitialDelay(Duration.ofSeconds(30))
}.build()
WorkManager.getInstance(applicationContext)
.enqueueUniqueWork(
"CALL_TRACKER",
ExistingWorkPolicy.REPLACE,
workRequest
//some code here
Will this implementation of using the addContentUriTrigger trigger start the WorkManager immediately just as the BroadcastReceiver will do or will it add some delay and start the WorkManager when the resources are available to the android system resources?
Moreover, is using the BroadcastReceiver the best solution to this, please let me know if a better solution exists. Can the Android team remove the permission to read Call information in future due to which this method might not work?
I need the details of all incoming calls for later processing.
Thanks for the help!
Links I have gone through:
addContentUriTrigger
addTriggerContentUri
Will this implementation of using the addContentUriTrigger trigger
start the WorkManager immediately just as the BroadcastReceiver will
do or will it add some delay and start the WorkManager when the
resources are available to the android system resources?
Most likely yes.
Looks like expedited requests might provide a way for you to prioritize the requests: https://developer.android.com/guide/background/persistent/getting-started/define-work#expedited, but even so, there's no guarantee about always fulfilling it. So you have to take into account that depending on resource availability some tasks might get deferred.
Moreover, is using the BroadcastReceiver the best solution to this,
please let me know if a better solution exists.
That depends how critical to you is the response speed to a phone call. I see in your sample you are setting a delay of 30 seconds, so maybe triggering the workmanager instantly it's not crucial?
Can the Android team remove the permission to read Call information in
future due to which this method might not work?
Yes, they can.

Suspend several functions invocations and use the returned value of first invoked in the rest of them?

Context: I have an app that sends the same request several times depending on user interaction.
I need to reduce as much as possible the IO operations, so my idea is to somehow enqueue the requests until the first one resolves. Then use the fetched data on the enqueued requests.
I think that one way to accomplish this would be using Java concurrency APIs (BlockingQueue, to name one).
I'm already using Coroutines and suspend functions and would be great use them for this, but sadly my current knowledge on those subjects is not enough yet.
Sample:
suspend fun getDataAndCalculate(): Int {
val data = remote.getDataFromServer()
return calculate(data)
}
Function getDataAndCalculate() can be invoked multiple times and from multiple threads, it would be great to somehow enqueue or suspend all these invocations while remote.getDataFromServer() is in execution. When remote.getDataFromServer() returns use data in all pending getDataAndCalculate() invocations.
¿Any idea or recommendation, please? ¿Is this recommended? ¿Do you know any design pattern to tackle this kind of problem?
Thanks!
(Not quite sure about the title, hope it describes what I want to accomplish, otherwise, I can improve it.)
One possible & simple solution I can think of:
Create an in-memory cache, simple save the returned value at your repository level. You can add a timestamp too, and verify at your suspend function level if you consider the response outdated.
Add a state which represents if a request is already running (a simple flag, like var isRunning: Boolean, and if it's already running just suspend until you have a cached value.
It might not be the best approach though, other simple solutions:
If you're familiar with Channels or Flows, you could write some debouncing logic probably
At your ViewModel level, check if the previous request finished or not. The same isRunning functionality, with the constraint that requests need to be launched from the same vm.
These can do the job, but it might not be the most elegant solutions

How to perform work manager tasks in sequence (when you don't have all work at the same time)

I want to upload some objects to a server. I'm using work manager and uniqueWork to avoid uploading duplicate objects. There is a constraint on the work so that they only operate when there is internet connectivity. The problem is I want each of these objects to upload one at a time, but all the work happens at once.
I know that I can use beginWith and workContinuations to perform work in sequence, but unfortunately multiple objects can be created at different times, so I do not have access to all the work at the time of creating the work.
val workRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setConstraints(networkConstraint)
.build()
WorkManager.getInstance()
.enqueueUniqueWork(uniqueName, ExistingWorkPolicy.KEEP, workRequest)
I assumed enqueue meant that all the work would happen one at a time like a queue. Is there a way to make it work that way?
You can use WorkManager's UniqueWork construct to be able to enqueue work so that it is executed only once. The Unique work is identified by the uniqueName.
In your case you should use a different ExistingWorkPollicy, as explained in the documentation: ExistingWorkPollicy.APPEND. This ensure that your new request is appended after other requests using the same uniqueName (this actually creates a chain of work).
val workRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setConstraints(networkConstraint)
.build()
WorkManager.getInstance()
.enqueueUniqueWork(uniqueName, ExistingWorkPolicy.APPEND, workRequest)

Is there a way to execute a chain of work items periodically in android WorkManager

I am developing an application and have based my content fetch on WorkManager. I have a chain of work items that i want to execute periodically. My concern is that as per documentation here
I can not use a periodic work inside a chain of work items , but can I execute a chain of work items periodically ?
I have explored multiple posts but have not found the exact answer.
Your PeriodicWorkRequest can in turn enqueue a chain of OneTimeWorkRequests. That's probably the best way to accomplish what you are trying to do.

Migrating from JobScheduler to WorkManager seems like a massive undertaking. Any advice+

I've used JobScheduler for a while now. A typical example would be:
JobScheduler triggers the onStartJob() method. This kicks off some sort of task that might include several other background processes (getting device position, making a network call, etc). I then use an interface to call back to the JobService once the task is complete or if it fails.
However, with WorkManager it seems like there is basically no way to run work asynchronously. I know WorkManager will run Workers in a separate thread but it seems like a single worker must run synchronously? If so I'll have to rework a lot of the logic in my app it seems unless I'm missing something.
Say I had a weather app and it would use the JobScheduler to run a JobService that did the following:
onStartJob() --> get device position --> make API request for local weather data -> write to database -> call jobFinished()
With WorkManager, is the intended implementation of something like this to chain multiple workers together? And if so, how do you pass data from one Worker to another? Again, seems like it would be a massive undertaking for any slightly complex app to migrate to the WorkManager API.
I think what you are looking for is a ListenableWorker. It has exactly that interface. Rather than you having to call jobFinished() in the end, we do the work for you when you resolve the ListenableFuture.
https://developer.android.com/reference/androidx/work/ListenableWorker.html#startWork() is analogous to onStartJob in your case.

Categories

Resources