1

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 ?

joghm
  • 569
  • 3
  • 20

1 Answers1

2

"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.

Yavor Mitev
  • 1,363
  • 1
  • 10
  • 22
  • Thanks for detailed explanation and provided links. In my case (in my application) If I run `adb shell am broadcast -a "androidx.work.diagnostics.REQUEST_DIAGNOSTICS" -p ""`, work that didn't run previously, starts immediately after I run that command. Why? – kv1dr May 31 '22 at 11:58
  • 1
    @kv1dr I am not sure. It might be device-specific. But the question is what constraint is Unsatisfied for you not to run? Because besides the work info you might search other places in the JS dump and find more info. For example, if you are in a low PowerBucket you might hit the QUOTA constrain for too much time using resources, or the CONNECTIVITY which is not only for the right network type, but also if you have used too many Network resources. There is info for the PowerBucket in each job info and separate sections for Quotas and Connectivity usages in milliseconds used. – Yavor Mitev May 31 '22 at 12:10
  • 1
    @kv1dr so for example you have hit the Quota constrain, you execute the above command - you go in a higher PowerBucket so you get more Quota and you are executed. This is just a guess. Try to check my comment here for more info about potential problems: https://stackoverflow.com/questions/72254896/android-recommended-and-reliable-api-for-periodic-background-work/72289497#72289497 – Yavor Mitev May 31 '22 at 12:10
  • It could definitely be Quota constrain. When I checked with the command in what bucket is my app, it was in Rare. But could max 1 work per day (but mostly is only 2 or 3 works per week) also reach the quota? My app is more of a type of "configure it and forget it" and user doesn't run it if notifications are working. And for those notifications I am using WorkManager. It doesn't need any connectivity at all. But notifications are max 1 per day, never more than 1, but not even that 1 work sometimes doesn't run. – kv1dr May 31 '22 at 12:36
  • 1
    @kv1dr now one can tell you because this $*1t - PowerBucket is different per each manufacturer. Even in the new versions, they use machine learning to say when you go in the bucket. See the end of my other comment and there you can see how to ask the user to exempt you from battery optimization. Also you might ask to be removed from AppHibernation. Also you can check www.dontkillmyapp.com for maybe some extra hacks which are device specific. Also in the QUOTA controller you can see something like that: #u0a785/2 from u0a785 ACTIVE, within quota, 600000ms remaining in quota – Yavor Mitev May 31 '22 at 12:48
  • 1
    @kv1dr this u0a785 you can see in the beginning of the job info. I think it might be the process id I guess. – Yavor Mitev May 31 '22 at 12:49
  • I am aware about some stupid battery optimizations done by some manufacturers. But I am testing it on Pixel 5. I think this phone is closer to AOSP, than others, and I thought Pixel phones doesn't have so "hard" optimizations like other manufacturers. Ok, thanks for that info regarding quota. For my app it says: #u0a739/241 from u0a739 RARE, within regular quota, 600000ms remaining in quota. Which looks like withing quota. Is it possible that I am scheduling too much works? Even if they are ran not so frequently? Since I see jobCOuntLimit=48 for rare? – kv1dr May 31 '22 at 13:11
  • @kv1dr it might be. But do you actually see QUOTAS as unsatisfied? Also, you should be able to see the number of executed jobs. There is info about it also. I am not sure how long it saves the data. Also, it is device-specific. I see that on newer devices I see much more info. I am not sure how it looks on the specific device you use. – Yavor Mitev May 31 '22 at 13:27
  • No, the only thing that I see under unsatisfied constraints is TIMING_DELAY. It just seems as some works were just not scheduled. I am really confused. I now limited number of scheduled works, so I can more easily read those logs. – kv1dr May 31 '22 at 13:39
  • 1
    @kv1dr I have edited my answer. Check it. – Yavor Mitev May 31 '22 at 14:04
  • Thank you very much for your detailed answers. It really helped me inspect issues with jobs. I think I figured out what is the problem on my side. I think I have to limit how many jobs I create. I have calendar of events, and for every events, we have to notify the user one day before event happens. I am creating new job for every event, more events, means more jobs. So i changed my logic a little bit, to create only job for first event, and after job finishes, notification is posted and next job is scheduled. For now it seams to work, but we will see if it keeps working. – kv1dr Jun 01 '22 at 06:03
  • 1
    @kv1dr great :) good luck :) – Yavor Mitev Jun 01 '22 at 06:46