2014-09-09 – This has been updated, see below.
Having recently written my first Widget for Android, here are some of the things I learned in the process.
Use a thread, not just a service
In the blog post introducing the AppWidget framework, you are encouraged to use a service to perform your widget updates if you are doing anything that might take a little longer, in order to avoid Application Not Responding (ANR) timeouts. However, this will usually not be enough. Since your service callbacks also run in your application’s main thread, you’ll still going to trigger ANRs if you do work there. Services really just declarative components, metadata that tell the system “I would appreciate if you wouldn’t kill me”.
The solution is to have your service start a separate thread. For an example, see Jeffrey Sharkey’s android-sky Widget.
Stop your service when you’re done
When your done with updating (if your using a thread, it’ll be when your thread has finished), make sure you stop your service. Nobody seems to be doing this, including the example code in the official blog post, nor sample projects by Android team members. As a result, so many widgets have their process run in the background all the time, not allowing Android to kill it to reclaim memory. Considering that RAM isn’t exactly a plentiful resource on devices like the G1, I would have thought this were especially important. Unless your updating multiple times an hour, I would strongly suggest that stopping your service and letting Android free up your process when necessary is the right thing to do.
Try to work around the bugs in Android 1.5 and the Launcher’s Widget implementation
It turns out there are quite a few of them, and they all seem to revolve around issues with deleting a widget. In particular:
- AppWidgetProvider fails to handle delete intents correctly. As a result, if you are subclassing it, your onDelete() method will not be called. The solution is to override onReceive() and handling delete intents yourself. Here’s a code snippet.
- In any case, if a user deletes an instance of your widget, the 1.5 default home screen will still keep sending update requests for the already deleted widget’s id, until the user adds a new instance of your widget (apparently, some internal bundle of widget ids is not reset on delete). This seems like this could result in a considerable waste of resources, or even impact your functionality, with you sending out widget updates that nobody is going to see. Note: I’m not sure if those obsolete widget ids are cleared out on reboot (probably). You might also not have this problem if you’re using custom scheduling instead of the default update facility.
The best one can do here appears to be keeping an internal list of deleted widgets (widgets for which you – hopefully – received a delete intent), and then just skipping those updates.
- Finally, there seems to be another issue with phantom widgets when a configure activity is used and the user cancels it. In this case also, you’ll continue to receive updates for the widget (which won’t be shown anywhere), except it seems even worse, because apparently this time around, widget will really exist internally, rather than just some internal state not being update correctly (so even a reboot won’t help here).
The work around seems similar: keep information around that indicates whether a widget as been configured successfully, and don’t bother updating widgets that haven’t.
2009-07-18: It appears there is another widget bug.
2014-09-09 – The 5 year update.
In my experience, this has all been fixed, with one small note.
- AppWidgetProvider.onDeleted is called correctly, no workaround necessary. Simply compile against any remotely recent Android SDK, you’ll be fine.
- People still complain about phantom widgets, but I cannot confirm this. I do not get any update requests from the system for widgets that have been deleted.
Here is what I know about phantom widgets.
AppWidgetProvider.onUpdate is called before the widget is configured
The documentation to this day claims that this is not so (“the system will not send the ACTION_APPWIDGET_UPDATE broadcast when a configuration Activity is launched“), but at least on my Sony Xperia with Android 4.4, this is not the case. Admittedly, Sony is using a custom home screen. At least you cannot rely on this, then.
The solution is simple though: Simply ignore that first update, that the docs claim shouldn’t be sent. Note whether a particular app widget id has been configured, then only process updates for widgets that have been configured.
When the user cancels the configuration activity, AppWidgetProvider.onDelete is called.
If you chose not to ignore that first update, that was sent before your widget’s configuration activity completed, you will get the onDelete callback once the configuration activity is canceled. Just be sure to set the RESULT_CANCELED return intent. Do this in onCreate, and then simply override the result value it when the user completes the configuration. This is much more simpler and more reliable than trying to cancel in onBackPress or onStop or onPause.
With the RESULT_CANCELLED value properly returned, I reliably receive onDelete callbacks, whether I exit the configuration activity via BACK or HOME.
If you use your own scheduler, you will still receive those events!
This was throwing me off first, but since I’m scheduling my own widget updates using AlarmManager, any such alarm already scheduled will still be delivered, even after the user has deleted your widget. To Android, your custom alarms have nothing to do with the widget, after all.
So you’ll either have to cancel those alarms, or, which is what I do, if only for historical reasons due to the bugs that existed five years ago, I mark the widget as deleted when it is removed, and subsequently ignore everything coming in for deleted widget ids.
10 thoughts on “Writing an Android Widget: What the docs don’t tell you”
Useful information. Thanks.
Nice blog. These are absolutely things to look out for when writing AppWidgets!
But, the fact that you have to spawn threads when using a service is something that the documentation DOES tells you:
“Like activities and the other components, services run in the main thread of the application process. So that they won’t block other components or the user interface, they often spawn another thread for time-consuming tasks (like music playback).” (http://developer.android.com/guide/topics/fundamentals.html)
Also, the onDelete() bug is in the documentation:
“Note: In Android 1.5, there is a known issue in which the onDeleted() method will not be called when it should be. To work around this issue, you can implement onReceive() as described in this Group post to receive the onDeleted() callback.”
Just for the record 😉
Do you know how to update a widget using broadcast(Intent) from a different, entirely separate app? I sure could use a code sample!
Thanks for your post!
I found your blog looking for a solution to a problem I have recently discovered on my Froyo 2.2 Droid 1. I am not an Android developer (yet) but I do know Linux and Java. I use Task Panel to watch what is running on my phone and I have noticed that I now have 3 processes that continuously restart on my phone. They are all related to 3 different widgets that I have tried out at one time, but I have removed from my desktop. But even though they are no longer there, the processes continue to restart. Are widgets registered somewhere and these did not get properly removed? Is there some way I can manually remove this registration short of removing the app associated with it because they are all still apps that I want, I just do not want them running all of the time. The apps/widgets in question are Google News and Weather, Meridian, and Slacker.
Thanks for any advice.
I would assume that most of the problems listed here have been fixed in 2.2, though I’m not sure why the processes keep restarting on your phone. Having a look at the logcat output may provide some indication as to what is going on (you ideally would install the Android SDK on your computer to analyze logcat; while you can do so on the phone, it isn’t particularly comfortable).
You could also try reinstalling the apps, and not ever trying the widgets this time around, and seeing if that makes a difference.
You may also be interested in my app “Autostarts”, which should allow you to disable the widgets, while keeping the rest of the apps installed and functional. It requires root though.
Having developped my own widget on 2.2, I found out those bugs (except the onDelete call) do still exists and deleted widgets keeps getting updated when scheduling is on, thus it’s normal you see those process being restarted all the time.
One solution is to restart your phone, this will clear the list of installed widgets and those that have been removed.
Or remove the package itself!
So far the only solution I found is to keep a list of deleted widgets and make sure those are not updated! Really a waste of resources!
Hi I want to add some animation(rotation, translation, scaling) as a part of my widget, is this thing possible because as per the documentation it seems that widgets are only to update the layout on some other process.
An IntentService sounds like the ideal candidate for what you describe. It runs on its own thread, and stops when it is done.