I am developing an Android app.
It should do some work every 1 minute. (60 seconds)
To do this, I used "RxJava - Observable.interval".
Observable.interval(0L, 60, TimeUnit.SECONDS, Schedulers.io())
.subscribe {
// do something
}
But actually, it doesn't work very exactly like...
2020-03-12 11:27:21.771
2020-03-12 11:28:21.787
There is a delay of about 0.016sec.
But it's ok, it is reasonable...
What I want to know is that...
If the performance of the device is lower and lower...
Can a delay of more than 1 or 2 seconds occur?
2020-03-12 11:27:21.771
2020-03-12 11:28:22.787 // <-- 1.016sec delay, is it possible?
If it can occur... how can I fix it?
Could changing the scheduler be the solution?
The scheduling of events in the Java environment is subject to delays; you will almost never get a a spot on delay to occur. The issue is that the clock resolution of the underlying operating system is probably no better than 10 milliseconds. Here, you are seeing a jitter of about 16 milliseconds, which is not surprising. This is not only a problem with RxJava.
If you read the documentation for your platform where it discusses sleeps, timers, etc, you will see that there are several notes and warnings. Usually the documentation will say that it will sleep for at least the given time.
In general, you have to determine how much drift you can support in your application. Then test over a sufficient interval (one sample isn't reasonable, but 10,000 may be).
You can find out more at Understanding RxJava Timer, Delay, and Interval Operators
Related
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.
I created a periodic weekly work manager worker to delete the files my application creates.
WorkManager workManager = WorkManager.getInstance(context);
workManager.cancelAllWorkByTag(workTag);
PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(DeleteFileWorker.class, 7, TimeUnit.DAYS).addTag(workTag).build();
workManager.enqueueUniquePeriodicWork(workTag, ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest);
Here, I'm telling the worker to run once a week or once each 7 days.
But when is it going to run? Can I control the approximate hour?
My guess is it will run the first time it can when the application starts and the second time will be at the same hour(approximately) and day as the first launch.
Can I configure it to run around 12 AM? It doesn't need to be an exact time.
You can't have strong expectations with WM. Please check here the Constraints section:
https://developer.android.com/topic/libraries/architecture/workmanager/how-to/debugging#use-alb-shell0dumpsys-jobscheduler
Also, let me point one thing - what you have "is not" a periodic Work.
What WM does is that it creates a single job in the JobScheduler and it has an extra Constrain - TIMING_DELAY. At the same time besides explicit constraints, you have implicit ones coming from the system. So for a work to be executed all the right conditions need to be in places related to battery optimizations, doze, power buckets, etc. So you can't say for sure when it will happen.
And when the work is executed successfully - WM creates a new job in the JS with TIMING_DELAY again the time you see as a period. So if you put 24 hours as a "period", but the conditions are not right for your work to be executed and it is delayed 10 hours - you will have a 34 hours span between 2 works.
Also, you know it is Android - on every device it is different. But you can improve this by asking your application to be excluded from battery optimization.
https://developer.android.com/training/monitoring-device-state/doze-standby#support_for_other_use_cases
Here more on what to expect from each device:
https://dontkillmyapp.com/
Also, have it in mind that you have a specific amount of time depending on your power bucket to run per day. I guess you are not using Network, but if you do - there is also a set amount of time to use the network. Without battery optimization disabled you might hit some of these:
https://developer.android.com/topic/performance/power/power-details
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.
In what way that I can have a real-time timer for task scheduling on Android?
Currently I am using Java.util.timer for my real-time application? However, the timer is missing firing when garbage collection is running or other other stuff. The description on java.util.timer says "this class does not offer guarantees about the real-time nature of task scheduling.". Then, which class that I should use on Android then? Or are there any other approach to have a real-time guarantee timer on Android?
Forgot to mention that the timer that I need is at 20 - 40 milliseconds precision level. That means, I need it to fire every 20 - 40 milliseconds.
Handler.postDelayed(Runnable r, long delayMillis);
Does not have a note about whether or not it guarantees it to happen on time. I've used this mechanism before and never noticed any inconsistency with it. However, i've only used it for relatively short intervals (less than a minute) If you are looking for something farther out than that I would suggest you take a look at the AlarmClock application in the Android source. That will probably give you a good approach. That being said if you are looking for extreme precision(down to seconds or farther) I doubt you're going to be able to find anything with guarantees. There are too many things that could be going on that could cause potential misfires.
I'm writing a sports app that needs to track the elapsed time of quarter/half/period. Elapsed time needs to be accurate to the second. The game clock needs to continue to run even if the user explicitly places the device in sleep mode by pressing the power button.
My first attempt at this involved using Handler.postDelayed() to trigger the clock ticks every 200ms and WindowManager.LayoutParms.FLAG_KEEP_SCREEN_ON to ensure that the "clock" wasn't stopped by a screen timeout. But I soon learned that it was possible to circumvent this approach by pressing the power button to manually put the device to sleep. In addition, the postDelayed() approach is experiencing some clock drift, apparently a result of the time spent in the run() method. The actual numbers are still accurate, but instead of being aligned, for example, on 5 second boundaries which are easily understood by users - the timers involved start to drift, resulting in some understandable user confusion.
After a bit of research I found techiques for using services, java timers, AlarmManager, and PartialWakeLock to implement timers. Services by themselves won't solve the problem associated with the device going to sleep. Java timers, like services, don't solve the problem with the device going to sleep. AlarmManager seems like a good approach, but I'm concerned that this isn't an appropriate use of AlarmManager (i.e., very short intervals between alarms). Using PartialWakeLock also looks promising, but by itself it doesn't address the clock-drift problem I'm experiencing.
I'm going to try a combination of AlarmManager and PartialWakeLock. The idea is that AlarmManager will help combat clock-drift and PartialWakeLock to help keep the code simple (fingers-crossed). I'm hoping that this approach will result in a reasonable balance between power conservation, code complexity, and user expectations. Any advice is greatly appreciated.
Thanks,
Rich
I've got a partial solution to my original post above. It doesn't yet address the clock drift associated with the time spent in calculations during the postDelayed() processing, but it is a step forward. In addition, it's deceptively simple, always a good sign.
It turns out I was using SystemClock.uptimeMillis() when I should have been using SystemClock.elapsedRealtime(). The difference between the 2 is subtle, but important.
As you might expect, my solution keeps track of elapsed time by accumulating durations between calls to postDelayed() - i.e., elapsed time = elapsedTime + lastClockInterval. As stated above, the original implementation used uptimeMillis(). Careful reading of the javadoc reveals that uptimeMillis() doesn't include time spent in "deep sleep", e.g., when the user presses the power button. But the elapsedRealtime() method does include time spent in "deep sleep" mode. All that was required to track time across deep sleep cycles was to replace the use of uptimeMillis() with elapsedRealtime(). Success! No need to use AlarmManager, PartialWakeLock, or anything else substantially more complicated. Granted, these methods still have uses, but they are overkill when implementing a simple elapsed-time clock or timer.
The next problem to tackle is with the clock-drift caused by the non-zero execution time associated with postDelayed() processing. I'm hoping that spawning a thread to do the processing will address this issue, allowing postDelayed() to more or less mimic an asynchronous call. Another approach would be to adjust the postDelayed() delay time to take into account the time spent in postDelayed(). I'll post my results.
On an unrelated note, during my investigation I treated myself to a CommonsWare Warescription. While I didn't directly use any ideas from this source for this problem, I do think that it is going to be my Android go-to information source for the foreseeable future. I've got an O'Reilly subscription through my day job, but I've found the CommonsWare books to be as least as good, if not better, source of information about Android development as the O'Reilly resources. And I have found the O'Reilly Safari resources to be pretty good. Interesting...
Cheers,
Rich
I'm sorry this is not quite an answer, but I feel your process is similar to mine, but there was little in regards to code to make it clear how you got around the sleep issue. I don't have drift, but the app does hang when it goes into sleep mode,then kind of resets forward when the display is active, then hangs again when the device sleeps. This is the core of the timer process.
Handler timerHandler = new Handler();
Runnable timerRunnable = new Runnable() {
#Override
public void run() {
// do something here to display
processTime(); // process what to be done on a sec by sec basis
try {
timerHandler.postDelayed(this, 1000
} catch (Exception ex){
}
}
};
Is there something here that I can do to allow it to continue when in sleep mode? This use to work on older version of android/devices.