2

I seem to have a hard time getting a straight answer on this so I'll make a more pointed question.

I want to create a timer that is start/stop/pause/resettable. If the timer is active, it should continue to be active even if I go somewhere else in the app or minimize the app. If the app is destroyed / shut down / closed altogether then the timer can be shut down.

I looked into AlarmManager but it doesn't seem to work right on all devices. Would a Service be a viable alternative / do what I need it to do? What would I use to actually perform the counting? A sleep command in a loop that sends an update command back to a BroadcastReceiver?

How would I get it to send a notification to the user when the timer hits 0?

  • https://stackoverflow.com/questions/36931965/how-to-run-a-timer-in-the-background – Quick learner Jul 25 '17 at 17:24
  • https://stackoverflow.com/questions/35578586/background-process-timer-on-android – Quick learner Jul 25 '17 at 17:24
  • "I looked into AlarmManager but it doesn't seem to work right on all devices" -- please provide a [mcve] demonstrating your use of `AlarmManager`, along with a technical explanation of what "doesn't seem to work right" means. You might also consider editing your question and explain, **in detail**, what the technical difference is between "If the timer is active, it should continue to be active even if I go somewhere else in the app or minimize the app" and "If the app is destroyed / shut down / closed altogether then the timer can be shut down. – CommonsWare Jul 25 '17 at 17:27
  • Trying to avoid AlarmManager since it doesn't work on all devices according to several SO posts – user8364053 Jul 25 '17 at 17:27
  • @CommonsWare I'd have to find it again but I read a few posts saying that on Samsung devices, there was some issue regarding AlarmManagers that had unexpected behavior – user8364053 Jul 25 '17 at 17:28
  • Here: https://stackoverflow.com/questions/34729966/alarmmanager-not-working-in-several-devices – user8364053 Jul 25 '17 at 17:28
  • try using handler in application class in the first example – Quick learner Jul 25 '17 at 17:29
  • That is not strictly a problem with `AlarmManager`; nothing else is going to survive those particular device settings either. That's one of the reasons why Google standardized this behavior with Doze mode and app standby on Android 6.0+. – CommonsWare Jul 25 '17 at 17:31
  • @CommonsWare Are you implying that on those devices, no matter what I do (Service, Timer, IntentService, AlarmManager, JobDispatcher, etc), they will kill the timer because those devices kill background tasks (unless I misunderstood the page)? – user8364053 Jul 25 '17 at 17:33
  • Possible duplicate of [How to run a timer in the background ?](https://stackoverflow.com/questions/36931965/how-to-run-a-timer-in-the-background) – HaveSpacesuit Jul 25 '17 at 17:56

1 Answers1

3

If the app is destroyed / shut down / closed altogether then the timer can be shut down.

You seem to think that this is somehow different than "go somewhere else in the app or minimize the app". They are not. Either your process is running, or it is not, and your process can be terminated at any point in time when your UI is not in the foreground. A foreground Service, tied to a Notification, is a typical pattern for keeping a process around as long as possible, but this is not an absolute guarantee.

Are you implying that on those devices, no matter what I do (Service, Timer, IntentService, AlarmManager, JobDispatcher, etc), they will kill the timer because those devices kill background tasks (unless I misunderstood the page)?

There are two ways of doing scheduled work:

  • In-process: Timer, ScheduledExecutorService, postDelayed(), etc.

  • Out-of-process: AlarmManager, JobScheduler

On all Android versions, when your process is terminated, any in-process timing goes away too. This is how most computers have worked, since the invention of the computer. Hence, these sorts of scheduling options are fine for cases where you only need the work to happen while your process is running (e.g., updating an on-screen elapsed-time counter).

On Android 6.0+, AlarmManager and JobScheduler are affected by Doze mode and app standby. On previous versions, they are supposed to be generally reliable. However:

  • Some device manufacturers effectively created their own Doze mode equivalents, and those manufacturers might wipe out alarms and jobs as part of their power-savings initiatives

  • Nothing survives a force-stop, which normally happens only if the user clicks "Force Stop" on your app's screen in Settings, but on some devices happens more often (e.g., the manufacturer tied some sort of built-in "task manager" to have force-stop behavior)

Hence, for devices where the manufacturer screwed around with Android, there may not be any form of reliable scheduling mechanism. In some cases, the manufacturers implementing precursors to Doze mode offered a whitelist mechanism, akin to how Doze mode does on Android 6.0+, where users could indicate apps that they would prefer not be impacted by the power-saving measures.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • `They are not.` How so? If I boot my device, for example, the app is not running. I click the app, now app is running. I might hit the back button to minimize, or home button, etc, app goes into background. I could open the task manager button and swipe the app off to the right / hit the X button to terminate the app. Are you saying that once I open the app for the first time it is always "on" somewhere under the hood even if I remove it from the active list? – user8364053 Jul 25 '17 at 17:52
  • My end goal here is that if someone starts the timer in the app, they can close out and go browse the internet, answer email, do whatever, but then when the timer hits 0, they get a notification / reminder / alarm / etc. – user8364053 Jul 25 '17 at 17:54
  • `Either your process is running, or it is not` Sure, but then what is the difference between in process and out of process? If my app is not running, wouldn't an out of process timer still be running? (this is what I am referring to perhaps -- if the app/process is terminated, there's no reason for the timer to still be counting) – user8364053 Jul 25 '17 at 17:59
  • @user8364053: "I might hit the back button to minimize, or home button, etc, app goes into background" -- and at this point, your process can be terminated at any time by the system. "If my app is not running, wouldn't an out of process timer still be running?" -- in general, yes, though Doze mode and equivalents play a role here. "if the app/process is terminated, there's no reason for the timer to still be counting" -- then you are saying that as soon as the user leaves your UI, that you no longer care whether the timer goes off, which runs counter to your second comment on this answer. – CommonsWare Jul 25 '17 at 18:01
  • `and at this point, your process can be terminated at any time by the system` Okay, didn't know that. So would the correct approach be to run a timer that is "out of process" (I assume this means "independent from the app") and it may trigger even if the app is terminated? (probably not a huge deal, honestly, if this is the case) – user8364053 Jul 25 '17 at 18:06
  • @user8364053: "I assume this means "independent from the app"" -- yes. `AlarmManager` and `JobScheduler` fill the roles of `cron`, Windows Scheduled Tasks, etc. The actual timing is managed by the OS, starting your service (or whatever) when the time arrives, forking a process for you if one is needed. "it may trigger even if the app is terminated?" -- I do not know what you mean by "the app is terminated". If you mean "the process is terminated", then yes, outside of Doze mode-style scenarios and force-stop scenarios. – CommonsWare Jul 25 '17 at 18:11
  • `I do not know what you mean by "the app is terminated". If you mean "the process is terminated", then yes,` Isn't the app a process? `outside of Doze mode-style scenarios and force-stop scenarios.` Not familiar with Doze or how it works, but is this something I have to account for, or something I have to just be aware exists and may override my timer when I minimize the app (or process, whichever is the right term here)? – user8364053 Jul 25 '17 at 18:13
  • I suppose I forgot to ask one other thing: The timer technically would need to be updated every second on the UI if the user is looking at the timer itself. Does AlarmManager allow for this? – user8364053 Jul 25 '17 at 18:14
  • @user8364053: "Isn't the app a process?" -- no, no more than a Windows `.EXE` file is a process. "or something I have to just be aware exists and may override my timer when I minimize the app" -- something along those lines. It is a complicated subject. "Does AlarmManager allow for this?" -- `AlarmManager` neither helps nor harms with this scenario. My guess is that you would use multiple techniques: an in-process timing solution for updating the UI every second-ish, and `AlarmManager` for the final "the time has elapsed" event. – CommonsWare Jul 25 '17 at 18:32
  • Unfortunately that's the very question I am trying to answer; AlarmManager does not seem to allow for this since the timer would need to be interactive. – user8364053 Jul 25 '17 at 22:51
  • @user8364053: I do not know what you mean by "interactive". If you mean "the user can start and stop the timer", you schedule the `AlarmManager` event when the user starts the timer, and you cancel the event when the user stops the timer. – CommonsWare Jul 25 '17 at 22:55
  • Interactive in the sense of being able to start, stop, reset, etc, yes. As far as I can tell, anything that would allow me to update something every second would imply I may as well skip the AlarmManager and go straight to a Service (I don't see a good way to integrate the AlarmManager with something that can request its state every second to update the UI) – user8364053 Jul 25 '17 at 23:01
  • @user8364053: "I don't see a good way to integrate the AlarmManager with something that can request its state every second to update the UI" -- it doesn't seem especially difficult (call one method on `AlarmManager` plus whatever you are using for your in-process timing). Your "go straight to a Service" basically means that your timer stops working once your process does. – CommonsWare Jul 25 '17 at 23:22
  • `plus whatever you are using for your in-process timing` but if I am using in-process timing what good is the AlarmManager? And the "whatever" part is the unknown! – user8364053 Jul 25 '17 at 23:23
  • @user8364053: "what good is the AlarmManager?" -- this [was covered already in this discussion](https://stackoverflow.com/questions/45309866/using-a-service-to-create-a-timer-that-runs-even-if-the-app-is-minimized/45310373?noredirect=1#comment77583630_45310373). "And the "whatever" part is the unknown!" -- I listed three options in my answer. All of this, and more, is covered in many books and courses on Android app development. – CommonsWare Jul 25 '17 at 23:29
  • With all due respect that does not answer the question; I honestly don't feel like what I am asking here is that complicated. I'm after a very common use case, but there are too many timer types out there and I don't know what does what I need it to do. I've looked at the AlarmManager pages and I am fairly confident it does not do what I need it to do here (namely with respect to interacting with it and having it send a signal back every second to update UI). – user8364053 Jul 25 '17 at 23:36
  • @user8364053: "With all due respect that does not answer the question" -- `AlarmManager` is how you ensure that when the timer is over, you get control, even if your process had been terminated along the way, wiping out your `Timer`, `ScheduledExecutorService`, etc. You have indicated that this is important to you. – CommonsWare Jul 25 '17 at 23:39
  • I don't know what's "important to me" on a technical level because apparently I am misunderstanding the difference between a process, app, service, when something is minimized or terminated, etc. I know what I need the thing to do functionally speaking in terms of what the user experiences. – user8364053 Jul 25 '17 at 23:41
  • @user8364053: "namely with respect to interacting with it and having it send a signal back every second to update UI" -- correct. It's not supposed to handle that. As I mentioned previously, you will need to use multiple techniques: `AlarmManager` as the failsafe timer-is-over mechanism **and** something else (`Timer`, `ScheduledExecutorService`, `postDelayed()`, etc.) for handling UI updates if you happen to have your UI in the foreground. – CommonsWare Jul 25 '17 at 23:42
  • My concern is that these multiple techniques cannot communicate with each other; once the AlarmManager is set, it does its own thing. If I call postDelayed separately I don't know how to make this update every second (I've used it in other contexts) nor do I know if I can have this communicate with the AlarmManager. – user8364053 Jul 25 '17 at 23:49
  • @user8364053: "My concern is that these multiple techniques cannot communicate with each other" -- why not? They both trigger code in your app. Your `AlarmManager` code raises a `Notification`, if your UI is not in the foreground. Your UI code is largely oblivious to the alarm event; it simply schedules the alarm events and updates your UI. "I don't know how to make this update every second" -- here is [a sample app](https://github.com/commonsguy/cw-omnibus/tree/v8.6/Threads/PostDelayed) from [a book](https://commonsware.com/Android) that covers using `postDelayed()` for a timing loop. – CommonsWare Jul 25 '17 at 23:53
  • @user8364053: Here is [another sample app](https://github.com/commonsguy/cw-omnibus/tree/v8.6/EventBus/LocalBroadcastManager) from the same book, showing how to use an event bus (`LocalBroadcastManager`, in this case), to raise a `Notification` if your UI is not in the foreground when an `AlarmManager` event occurs. – CommonsWare Jul 25 '17 at 23:54
  • Because the UI and the AlarmManager need to be "in sync" -- the UI can't be oblivious of the alarm because it needs to be able to update with how much time is left before the timer hits 0. – user8364053 Jul 25 '17 at 23:56
  • @user8364053: "the UI can't be oblivious of the alarm" -- sure it can. It is using its own separate timing mechanism for the UI updates. It knows when the final gun sounds on its own, irrespective of `AlarmManager`. You seem to be insistent that you are only going to track time one way, and that's not going to be possible given your desired feature set. – CommonsWare Jul 26 '17 at 00:00
  • So correct me if I am wrong, but are you saying that basically you have two timing methods: One timer in the UI, one timer as an AlarmManager, which start at the same time. They both run independent on each other, with the idea being that if you're looking at the UI, then the UI timer finishes and you can do whatever. If you're minimized, then the AlarmManager code finishes and sends a notification. But let's say the AlarmManager finishes before the UI timer. I get the notification, I go back to my app UI. It still shows some time left on the clock because they are separate timers. – user8364053 Jul 26 '17 at 00:04
  • And then if I want to pause or reset the timers, I have to do it in both places (stop the UI countdown, stop the AlarmManager, reset the time in the UI, reset the time in the AlarmManager, etc) – user8364053 Jul 26 '17 at 00:06
  • Just tried implementing the `postDelayed()` part using similar structure as the sample app -- the timer stops updating when you minimize, leading to a mis-sync – user8364053 Jul 26 '17 at 00:18
  • !! Use ScheduledExecutorService. It is good for this task. I used it, it worked out great!. – harsh Aug 29 '21 at 07:58