AlarmManager / App Widget / Lockscreen / Text Clock

TextClock Version 2 – Part 5

In the previous article I mentioned an issue that has developed within TextClock – the time is actually updating anything up to a minute later than it should be. In this article in this we’ll investigate what is happening.

widget-smallFirstly it is worth mentioning that this has been rather a nasty issue to track down. It has taken quite a bit of trial and error of different solutions before, somewhat accidentally, stumbling across the cause. Once the true cause had been identified, the fix was relatively quick to implement but, as we shall see, this has involved much frustration.

My initial thoughts we that the issue was due to the fact that I was using an IntentService which was being triggered by a PendingIntent from an AlarmManager alarm. I saw an answer to an issue on stackoverflow.com by the always knowledgable and helpful CommonsGuy which indicated that starting a Service from an alarm was not as reliable as using a BroadcastReceiver instead. Of course, I am unable to find the answer now, so am unable to include a link.

That appeared to tie in with a warning that I was seeing when the app widget was initialising:

It therefore seemed likely that simply switching from an IntentService to a BroadcastReceiver to be woken by the alarm may resolve the issue. Unfortunately when I tried this, I still saw the same delay on the alarm firing.

Sebastiano Poggi suggested registering a receiver for ACTION_TIME_TICK broadcast event which fires every minute. Initially this looked promising, but my receiver kept getting de-registered. The problem with ACTION_TIME_TICK is that it is not possible to register a receiver for this in the Manifest, and so the context through which it is registered must remain active in order for any receivers registered through it to remain active. Part of my design criteria was that I didn’t want to keep long running services which may be prone to be shut down by the OS, so using a programatically registered receiver was not an option.

That was when I turned my attention back to AlarmManager itself, as I have used the AlarmManager / IntentService combination quite successfully in the past and have memories of TextClock version 1 working perfectly. It was only when reviewing the documentation for AlarmManager#setRepeating(). The documentation now states that setRepeating() is not suitable for precise timing operations, but that Handler should be used instead. As with ACTION_TIME_TICK, Handler really is not an appropriate solution for this application. However, there is a new method in AlarmManager named setExact() which reproduces the old behaviour of set().

Further to this, there is also a note at the top of the AlarmManager documentation which states:

Note: Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.

While I normally just stick to howto’s on Styling Android, I feel compelled to pass comment on this change to AlarmManager. While I can completely understand the logic behind the change in terms of marshalling alarms together to reduce battery consumption, I firmly believe that simply changing the way existing methods work is completely wrong, and awful API design. It is a little like putting your car in to the garage for a service, and when it is returned they have switched the operation of the brake and accelerator pedals. To simply change the behaviour of existing methods just causes existing apps, which have worked perfectly before API 19 to work differently than they did before. This will result is developers having to fix bugs that are effectively introduced with changes to the OS. This kind of change really is not difficult to manage. In this case, the set() method could have been deprecated, and replaced (without changing it’s current behaviour) with two methods: setExact() which encompasses the existing, exact alarm functionality; and setInexact() which encompasses the new, more efficient functionality. The compiler would then have alerted the developers of the change because of the deprecation, and this would have served as an active prompt to review the requirements of the app and use the appropriate method. If deprecations had been used in this way I would have been alerted to the behavioural change by the compiler and it would have saved me a significant amount of time in trying to understand why the behaviour of TextClock suddenly changed.

OK, rant over, in the concluding article in this series we’ll look at the changes necessary to get TextClock working again.

TextClock is available from Google Play. Version 2.0.3 has recently been published which contains the fix to the AlarmManager issue.

© 2014, Mark Allison. All rights reserved.

CC BY-NC-SA 4.0 TextClock Version 2 – Part 5 by Styling Android is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Permissions beyond the scope of this license may be available at http://blog.stylingandroid.com/license-information.

2 Comments

  1. It’s important to review SDK changes when bumping your targetSdkVersion. If you were still targeting 18 or lower, the behavior would not have changed even on KitKat devices. That’s how they get away with modifying the behavior of existing APIs.

    “Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested”

    1. Believe it or not, as someone who writes a blog on Android development, I do analyse API changes between SDK releases quite closely, and I still missed this change. My whole point is that better API management through deprecation will alert developers to API changes via the compiler.

Leave a Reply

Your email address will not be published. Required fields are marked *