Daily Job on a specific time for Android 8.0+ - android

I’ve been trying to figure out how to use any of background task APIs in order to create a Work Request that fires every day on a specific time of the day on devices running Android 8.0+.
The time must be set by the user locally on the device (meaning no GCM).
Is that even possible anymore? Legacy devices with AlarmManager works wonders, but as far as I am aware all pending intents are cleared when the app gets Force Stopped so it’s a no go now.
WorkManager's PeriodicWorkRequest doesn't allow you to set the time to start the Work. I looked into creating a delayed OneTimeWorkRequest and schedule the next one after it runs, but nothing ensures that the work will run on the time I want.
Evernote's Android-Job library has issues with the DailyJob too (https://github.com/evernote/android-job/issues/318).
Any suggestions/ideas more than welcome.

New PeriodicWorkRequests now support initial delays. So you take the required time from the user, calculate difference between userTime and Now and just set the delay.
Unlike AlarmManeger it is very reliable.
The only problem is Android fires Worker when it finds the best moment for it (no other work to do), so it may be started with a little delay (usually not more than a few minutes).

You could just use a OneTimeWorkRequest which schedules a copy of itself before it returns a Result.
class TestWorker: Worker() {
fun doWork(): Result {
val nextWorkRequest = OneTimeWorkRequestBuilder<TestWorker>()
nextWorkRequest.withInitialDelay(24, TimeUnit.HOURS)
WorkManager.getInstance().enqueue(nextWorkRequest)
return Result.SUCCESS;
}
}
Start off the first work request with a:
val firstWorkRequest = OneTimeWorkRequestBuilder<TestWorker>()
firstWorkRequest.withInitialDelay(24, TimeUnit.HOURS)
WorkManager.getInstance().enqueue(firstWorkRequest)
Thus you get something which looks like a PeriodicWorkRequest with the timing you need.

Did you try using chained tasks in WorkManager. First Schedule OneTimeWorkRequest, then chain it with PeriodicWorkRequest. Like:
WorkManager.getInstance()
.beginWith(workA)
.then(workB)
where workA is OneTimeWorkRequest and workB is PeriodicWorkRequest

Related

Can tasks on PeriodicWorkRequest be prioritised?

We utilise a periodic Worker to fetch the location every 15 minutes.
As long as the phone is actively used and internet connection is available, everything goes by the books.
But when the app is in the background for too long and or even worse when it's in the flight-mode, the gaps between the fetches become as long as couple of hours.
I believe this is because the work is batched and it's up to the Android phone to decide when to run it again.
According to the documentation setExpedited() can in fact run a job under a higher urgency and prevent it from being batched.
Marks the WorkRequest as important to the user. In this case,
WorkManager provides an additional signal to the OS that this work is
important.
val workRequest = PeriodicWorkRequestBuilder<WappWorker>(15, TimeUnit.MINUTES).addTag(
TAG
).setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST).build()
I have implemented it this way and successfully compiled and ran it.
But it crashes at runtime:
java.lang.IllegalArgumentException: PeriodicWorkRequests cannot be
expedited
How odd. So this feature seems to be only allowed for OneTimeWorker and not for the PeriodicWorker.
What are my options? Is there any other way to mark my periodic worker jobs with a higher urgency? Thanks

When the android app is force stopped and reopened, will WorkManager executes overdue work or startover?

Assume there is a scheduled work like this:
PeriodicWorkRequestBuilder<RefreshAuthWorker>(
repeatInterval = 15L,
repeatIntervalTimeUnit = TimeUnit.MINUTES,
flexTimeInterval = 5L,
flexTimeIntervalUnit = TimeUnit.MINUTES
)
Let's say, At 10th minute, I forcely killed/stopped the app.
Then reopen the app again after 10 mins.
by this time, the existing/killed work is overdue by 5 mins.
Now, What will the WorkManager do?
1.WorkManager respects the missed overdue work and do it immediately and then schedule the next work.
(or)
2.WorkManager ignores the past overdue work and schedules the next work?
This is a very important scenario to me because, let's say I have to refresh a token every 15 mins. But if WorkManager does like said in 1st point then, by the time I reopen the app, the token is already expired 5 mins ago and the next work is going to happen in 15 mins. So, it's a total of 20 mins with an expired token.
Can somebody who knows any idea what will the WorkManager do in such scenario, please help.
You can't do such things with a WorkManager.
Everything might happen if an application is forced stop. It is a problem of the user if he decides to do so. You should not care.
Are you sure you do not mean - clear from recent?
The priority of WM is saving resources like battery and network data. Timing is not a big concern. The idea is that you need some work to be executed for sure at some point. Like you want to upload a picture to a server.
What WorkManager does is - it creates a job in the JobScheduler. The job is executed when all the constraints are satisfied:
You have implicit constraints related to battery saving. You have some amount of resources that you can use based on your Power Bucket level. Also, the device's state is important. You can't predict when these constraints will be satisfied.
Also you have explicit constraints that you set. Like Connectivity, Battery level and in your case: "a period". But is a no period at all. When you have a "period" work on a higher level in the WorkManager - actually it means - many single jobs in the Job Scheduler. And each one of these jobs has the above implicit constraints and your explicit which is called - timing delay. So you see:
You start the work
WM schedules a job in JS with respective constraints
After 15min timing delay is satisfied
No one can tell what is the status of the implicit constraints. The device might be dozing, or you might have used all of your data usage for the last 24 hours or something like this.
5 At some point when all the constraints are satisfied - the Work starts and when it is finished:
6. You have a new job with the same constraints as before. So in theory your "15min period work" might be executed in 24 hours and after that, it might execute the second time in 15 minutes.

Workmanager OneTimeWorkRequestBuilder not working with long duration in setInitialDelay

i am using Workmanager to perform an action every midnight , i am using it like that :
val duration = midNight.time - now.time
val workRequest =
OneTimeWorkRequestBuilder<ActionWorker>()
.setInitialDelay(duration, TimeUnit.MILLISECONDS)
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork(
WORKER_ID,
ExistingWorkPolicy.REPLACE,
workRequest
)
and inside the ActionWorker doWork i write to the sharedpreferences, the problem is when duration is relatively short time like 2 to 5 hours ,action is performed perfectly, otherwise if the duration is set to 12 hours for example, the action is not performed .
i set duration to half an hour and i tried killing the process using
adb shell am kill "package name"
and the task is still performed well after half an hour, does anybody have an idea why it get lost when it is set to long duration ?
"not working for long durations" is not your problem. Try to debug the job scheduler. As WorkManager uses jobs underneath:
https://developer.android.com/topic/libraries/architecture/workmanager/how-to/debugging
Execute:
adb shell dumpsys jobscheduler
And check:
Unsatisfied constraints: TIMING_DELAY CONNECTIVITY [0x90000000]
If TIMING_DELAY is satisfied, and I am sure it will be - there might be other reasons why the Work is not executed. There are a lot of restrictions from Android like how many times a work can be executed per 24 hours or how much Network usage you are allowed. Check here:
https://developer.android.com/topic/performance/power/power-details
Are you also sure what you want to achieve? Why every night? Check:
https://developer.android.com/reference/androidx/work/Constraints.Builder
In general, the idea is for you to pick the conditions - idle/not idle device, Wi-Fi/no Wi-Fi, charging/not charging, low battery/not low battery, and then Android will decide when to execute. If there is something related to the server-side requirements I would get it. But otherwise, I don't.
And then you set the requirements, put a periodic job with a period of 24 hours and some flex period of 2-3 hours and you are done.
EDITED:
How to debug TIMING_DELAY problems:
check the following fields in the job info:
Minimum latency: +19m59s941ms
Enqueue time: -1m47s251ms
Run time:earliest=+18m12s690ms, latest=none, original latest=none
That is how you can see when the work was scheduled, what delay is set and when you should expect the TIMING_DELAY to be satisfied. If it is not what you expected - check again what you have set or check if you are now in the case of failing and retrying works. If it is retrying the minimum latency comes not from what you have set for a period/delay, but from the policy settings. More on this here:
https://developer.android.com/topic/libraries/architecture/workmanager/how-to/define-work#retries_backoff
Also, you should have in mind that the lifecycle of the Work is bigger than the one of the job. One Work can create and trash many jobs. You have a job for a single work. The work fails and needs to retry - a new job is scheduled. You can observe this more easily via:
Background Task Inspector
https://developer.android.com/studio/inspect/task
Here you can see the number of retries, etc.

Notification with workManager

I'm creating application using PeriodicWorkRequest to execute or fire notification with content changing every day.
it work smoothly but i want to test the content of next day, i search in this case but does not arrive to the best solution. i try to change the day in device but it still does not work
val notificationWorkBuilderRepeated = PeriodicWorkRequest
.Builder(Notification::class.java, 1, TimeUnit.DAYS)
.setInputData(inputData)
.build()
WorkManager.getInstance().enqueue(notificationWorkBuilderRepeated)
The best approach here is to write some androidTest using the WorkManagerTestInitHelper.
This helper makes available a custom driver that you can use to test PeriodicWorkers simulating that the periodic delay has been met:
val testDriver = WorkManagerTestInitHelper.getTestDriver()
// Tells the testing framework the period delay is met
testDriver.setPeriodDelayMet(request.id)
As I have worked for first time with Work Manager I also got this problem for testing.
I have just decreased interval for 15 minutes and tested.
Currently its working fine for every hour.
You can also test it by giving 15 minutes (minimum interval) to test it.
Hope it will helps you. Thank you.

Workmanager does not execute unique jobs with APPEND policy

I've found this weird behaviour of work manger (alpha-12) that it does not execute a job after it was enqueued. The code to enqueue the work is below.
fun enqueue(phoneNumber: String?,priorityId: String? = null): ListenableFuture<WorkInfo> {
return WorkManager.getInstance().run {
val work = OneTimeWorkRequestBuilder<ContentDownloaderWork>()
.addTag(TIMESTAMP)
.setInputData(workDataOf(PHONE to phoneNumber, PRIORITY_ID to priorityId))
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.SECONDS)
.setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
.build()
beginUniqueWork(TIMESTAMP + priorityId.orEmpty(), ExistingWorkPolicy.APPEND, work).enqueue()
getWorkInfoById(work.id)
}
}
I enqueue this work each time on app startup. And what I am doing is killing my app in the middle of the work process that is downloading files from server. It successfully restart every time I reopen my app but for limited number of times.
It seems like it has some sort of queue which gets filled up and does not allow any more jobs to be enqueued. When I check all available jobs by tag it show me that some are canceled and some succeeded but when I try to enqueue a new work it does nothing.
So is it a bug in workmanager or am I doing something wrong?
One important information that is missing in your description is the Android version that you're using in these tests.
WorkManager schedule workers using the JobScheduler API on Android Marshmallow (6.0, API Level 23) and newer, and it schedules a maximum amount of workers using this API at any time (default is 20, maximum is 50). The threshold you're seeing may be linked to this value.
Another important point, is that WorkManager keeps tracks of your workers for you so, if the application is killed while the worker is running, WorkManager automatically restart the worker. You don't need to do anything.
What is not clear to me, is what are you trying to achieve appending a new job every time at the application startup.
Last point, the backoff criteria of 1 second seems a bit aggressive to me. The current default value, looking into the source code, is 30 seconds and the maximum is 5 hours.
However, if you think that there're something wrong (or documented incorrectly) I suggest you to open a bug on WorkManager's public issuetracker.,

Categories

Resources