1. It will reduce the battery life of the phone.
2. Handled badly and your widget could end up making the phone completely unresponsive. Pushing updates to onscreen widgets is a somewhat costly process.
Now that you've read and understood the implications, let's move onto how to use an AlarmManager. Normally, when setting up an AppWidget, you will supply the update interval in milliseconds (as updateTimeMillis). There are two disadvantages to this. The first, as pointed out, is the minimum of 30 minutes (you'll see no warning about this if you lower it, by the way). The second, less obvious one, is that the time isn't predictable. That is to say, if you wanted to widget to always update once an hour, on the hour, it's near-impossible. Likewise, if you wanted to widget to update at 0, 15, 30 and 45 minutes past each hour, you can't do that, either.
Here's how we're going to solve this problem (note: this tutorial assumes that you already have knowledge of app widgets and services).
public class MyWidget extends AppWidgetProvider { private PendingIntent service = null; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { final AlarmManager m = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); final Calendar TIME = Calendar.getInstance(); TIME.set(Calendar.MINUTE, 0); TIME.set(Calendar.SECOND, 0); TIME.set(Calendar.MILLISECOND, 0); final Intent i = new Intent(context, MyService.class); if (service == null) { service = PendingIntent.getService(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT); } m.setRepeating(AlarmManager.RTC, TIME.getTime().getTime(), 1000 * 60, service); } @Override public void onDisabled(Context context) { final AlarmManager m = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); m.cancel(service); } }and a cut down service example. The service is where we perform our logic and update the AppWidget.
public class MyService extends Service { @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { buildUpdate(); return super.onStartCommand(intent, flags, startId); } private void buildUpdate() { String lastUpdated = DateFormat.format("MMMM dd, yyyy h:mmaa", new Date()).toString(); RemoteViews view = new RemoteViews(getPackageName(), R.layout.widget); view.setTextViewText(R.id.lastUpdated, lastUpdated); // Push update for this widget to the home screen ComponentName thisWidget = new ComponentName(this, MyWidget.class); AppWidgetManager manager = AppWidgetManager.getInstance(this); manager.updateAppWidget(thisWidget, view); } @Override public IBinder onBind(Intent intent) { return null; } }(note: you should think carefully before making your service START_STICKY, as this means the service should be explicitly stopped. Since we're running a one off procedure, we want to have this hanging around as little as possible. Don't be tempted to use a BroadcastReceiver here, as these are designed for very fast operations. Anything taking more than about 10 seconds will be seen as misbehaving by the Android OS and might be killed off)
Our Alarm is set up in the widget's onUpdate method. It shouldn't look that much different to how a standard AppWidget call to a service looks, except for that we're scheduling the alarm to do the work. Note that we're telling the widget to repeat the call once a minute. This is fine for our example, since you don't want to sit around for 15 minutes to see the results. However, in real life you will want it to do so less frequently.
We're also telling the alarm to kick off immediately. If we want it to kick off at an exact time, we'll need to create it. The simplest way is with the calendar:
Calendar TIME = Calendar.getInstance(); TIME.set(Calendar.MINUTE, 0); TIME.set(Calendar.SECOND, 0); TIME.set(Calendar.MILLISECOND, 0); m.setRepeating(AlarmManager.RTC, TIME.getTime().getTime(), AlarmManager.INTERVAL_HOUR, service);The above will kick off the service once an hour, on the hour. You'll notice that the widget is updated immediately, which is normal. It does this because it "missed" it's previous alarm of the last hour. It'll then sit quietly until the next update is due.
If you want the alarm to issue more frequently then you'll need to adjust the intervalPeriod.
Finally, we come to an important step: how to stop the alarm! You'll notice that in our class we have declared:
private PendingIntent service = null;which is set up later in the onUpdate (if it's not null). We need to keep hold of this so that we can cancel the alarm later! We do so in the onDisable() method which will be triggered when all widgets are removed from the screen. We pass the PendingIntent to the cancel method of the AlarmManager so that it can determine which alarm it must cancel.
And there you have it. Pretty straight forward, really. If you have already created an AppWidget that calls a service, then you'll find you need only add in a handful of lines of code to have to AppWidget update more frequently, or at a more predictable interval.
This is a very comprehensive tutorial. I was actually one of the people who had this question in my mind about how to update an Android widget more often than the restricted 30 minute interval. Thanks for providing the answer.
ReplyDeletewhite label seo
This comment has been removed by the author.
ReplyDeleteSorry, but this not works. onDisable is invoked from the different instance where service is null :(
ReplyDeleteAh, a minor bug in the code. Best to check for the service being not null before attempting to disable it ;)
ReplyDeleteThere is no NPE, so app not crash. Bug is, cause alarm still working even after widget off. Unfortunately I can't fing beauty sollution for this.
ReplyDeleteIs it possible to do the same update without service.
ReplyDeleteYes, it is possible to do this without using a service.
ReplyDeleteHowever, you need to keep in mind that if you do all your work in the app widget's onUpdate method, then you could cause the entire UI to lock up, if it is a long-running task.
I'd recommend a service myself, or, if you really don't want to use one, make sure that you put all your work into a separate thread. Also, you will be limited to updates every 15 or 30 minutes at most (as mandated by the Android system). A service will enable you to do it more often.
private PendingIntent service is always null due new instances are being created upon any event. So onDisable will no switch the manager off. Use static field here.
ReplyDeleteI think that's a very good point to make and you make it well.
ReplyDeleteView Document
In my app I want to wake up my app everyday @ 12am and do some activity.How can I use Alarm Manager to wake up my app everyday @ 12am?...please help me. thanx in advance.
ReplyDeleteI've only ever used the AlarmManager in conjunctions with widgets (as in the above example). If you want to do the same thing, it's simply a case of setting the alarm to 12:00am:
ReplyDeleteCalendar TIME = Calendar.getInstance();
TIME.set(Calendar.HOUR, 0);
TIME.set(Calendar.MINUTE, 0);
TIME.set(Calendar.SECOND, 0);
TIME.set(Calendar.MILLISECOND, 0);
and the interval to
AlarmManager.INTERVAL_DAY
That will make the app run at 12am every day.
However, if you don't want to use a widget, then you will need to make use of broadcasts in your app to set up the alarm when the phone has completed booting. You'll need to investigate using:
android.intent.action.BOOT_COMPLETED
Unfortunately, I don't have any experience in using that, myself :|
I am not sure if I'm right. But from what I know, onUpdate will be called more than once. And everytime it's called you're setting a repeating intent right? If we can do it in onEnabled() instead of onUpdate() wouldn't that work?
ReplyDeleteAh, something that's not mentioned is that the widget_provider.xml file used by the widget in this case will contain the line:
ReplyDeleteandroid:updatePeriodMillis="0"
which tells the widget never to update automatically. Since we're doing the update in the service ourselves, this is what we want to do.
Thank you for the great succinct article. I think your last comment about android:updatePeriodMillis should be included in the article because I scratched my head for a while before finding this comment answering my question that is the same as of Sudarshan.
ReplyDeleteVery Good tutorial I have implemented this and it's working fine. It save my time,
ReplyDeleteThank developer for this great work