I'm stumped. I know this question has already been answered a hundred times but nothing I've tried works.
My question: I made an Android widget that needs to refresh precisely at each minute, much like all clock widgets do. (This widget tells me in how many minutes are left before my train leaves, a one minute error makes it useless).
Here are my attempts to far, and the respective outcomes:
- I put
android:updatePeriodMillis="60000"
in myappwidget_info.xml
. However, as specified in API Docs, "Updates requested withupdatePeriodMillis
will not be delivered more than once every 30 minutes" and indeed that's about how often my widget gets updated. I tried using an
AlarmManager
. In myWidgetProvider.onEnabled
:AlarmManager am = (AlarmManager)context.getSystemService (Context.ALARM_SERVICE); Calendar calendar = Calendar.getInstance(); long now = System.currentTimeMillis(); // start at the next minute calendar.setTimeInMillis(now + 60000 - (now % 60000)); am.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), 60000, createUpdateIntent(context));
however as stated in the API docs, "as of API 19, all repeating alarms are inexact" and indeed my widget actually gets updated every five minutes or so.
- Based on the previous point I tried setting
targetSdkVersion
to 18 and saw no difference (updates every five minutes or so). The
setRepeating
documentation seems to recommend usingsetExact
. I tried the following. At the end of my update logic:Calendar calendar = Calendar.getInstance(); long now = System.currentTimeMillis(); long delta = 60000 - (now % 60000); Log.d(LOG_TAG, "Scheduling another update in "+ (delta/1000) +" seconds"); calendar.setTimeInMillis(now + delta); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); alarmManager.setExact(AlarmManager.RTC, calendar.getTimeInMillis(), //UPDATE_PERIOD_SECONDS * 1000, createUpdateIntent(context));
It works perfectly for a couple minutes and then reverts to updating every five minutes or so (and not even near minute changes). Here are some timestamps of when the update intent is received:
- 21:44:17.962
- 21:52:37.232
- 21:59:13.872
- 22:00:00.012 ← hey suddenly it becomes exact again??
- 22:01:47.352
- 22:02:25.132
- 22:06:56.202
Some recommend using a
Handler
. I defined aService
which I start when the widget provider is enabled, and does this after update code:int delay = (int)(60000 - (System.currentTimeMillis() % 60000)); Log.d(LOG_TAG, "Scheduling another update in " + delay/1000 + " seconds"); new Handler().postDelayed(new Runnable() { public void run() { Log.d(LOG_TAG, "Scheduled update running"); updateAppWidget(); } }, delay);
and this one works perfectly for several hours, but then the service gets suddenly killed and gets "scheduled to restart after HUGE delay". Concretely, the widget just gets stuck at some point and doesn't get updated at all.
Some other options I've seen online: the linked post above suggests creating a foreground service (which, if I understand correctly, means having a permanently visible icon in my already crowded status bar. I don't have one permanent icon for each clock widget I use so that should not be necessary). Another suggestion is to run a high priority thread from the service, which feels awfully overkill.
I've also seen recommendations to use Timers
and BroadcastReceiver
s but the former is said to be "not appropriate for the task" and I remember having trouble doing the latter. I think I had to do it in a service and then the service gets killed just like when I use Handler
s.
It should be noted that the AlarmManager
seems to work well when the phone is connected to the computer (presumably because it means the battery is charging), which doesn't help because most of the time I want to know when my train will leave is when I'm already on the way...
As the Handler
is perfectly accurate but just stops working after a while, and the AlarmManager
option is too inaccurate but does not stop working, I'm thinking of combining them by having AlarmManager
start a service every ten minutes or so, and have that service use a Handler
to update the display each minute. Somehow I feel this will get detected by Android as a power hog and get killed, and anyway I'm sure I must be missing something obvious. It shouldn't be that hard to do what's essentially a text-only clock widget.
EDIT: if it matters, I'm using my widget on a Samsung Galaxy Note 4 (2016-06-01) with Android 6.0.1.
setRepeating()
and gone forsetExactAndAllowWhileIdle
to counter this, but still – AtionAlarmManager
which updates sometimes every minute, sometimes slower (up to five or six minutes). How clock widgets can work is a mystery to me. – Hydrotherapy