I'm going to create widget which needs to update its content every minute (it shows time-related data).
However, there is no need to update widget if it is currently invisible, which means:
- screen is turned off
- another app is running
- widget is placed on another (invisible) home screen tab
What is the best way to update only visible widget every minute - without waking up device nor doing unnecessary computations? After widget becomes visible, small lag before update is acceptable.
To keep from updating when the screen is off, use the AlarmManager to schedule a recurring alarm that doesn't wakeup the phone.
The other two bullet points you have in your question aren't possible. There is no way to detect if your widget is on a home screen that isn't currently visible, and there is no way to determine if an app is running that is hiding the home screen. I have filed a ticket on http://b.android.com requesting this functionality be added to Android. If you feel like starring it, it will help it gain priority: http://code.google.com/p/android/issues/detail?id=5529&q=reporter:mark.r.baird&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
- You can ask the PowerManager about isScreenOn()
- You can register for Intent ACTION_SCREEN_OFF/ACTION_SCREEN_ON and turn you timer on/off accordingly.
Allthough the answer above concerning the AlarmManager is correct it may not be sufficient since I observed many phones also delivering alarms even if they are not of type *_WAKEUP. This may happen if there are other applications installed that are waking up the device. And if it is once awake, it delivers all pending alarms.
I found this under a project named 24clock in goole code. It try Not update the widget while user is away from home:
ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> runningTasks = am.getRunningTasks(2);
for (RunningTaskInfo t : runningTasks) {
if (t != null && t.numRunning > 0) {
ComponentName cn = t.baseActivity;
if (cn == null) continue;
String clz = cn.getClassName();
String pkg = cn.getPackageName();
// TODO make this configurable
if (pkg != null && pkg.startsWith("com.android.launcher")) {
return true;
}
return false;
}
}
It might be an answer for you requirement #2. Although it might not work under other 3rd party launchers.
== Update ==
A few months later, I suddenly have an idea in my mind about how to detect whether home screen is showing. I put it in my blog, and I ran test on my Desire and it works fine. The method is to query the Android for all installed packages, which one of it contains the "Launcher" ability, and then check whether it is on the top of running stack. If anyone have tested please let me know the result as well, since I have no access to other Android devices.
The accepted answer from mbaird hits the nail on the head. The proposed onVisivilityChange()
method, if implemented, should cover all the cases above.
In the meantime this is still a real problem for some types of widget. jom introduced the possibility of registering to receive ACTION_SCREEN_OFF/ACTION_SCREEN_ON intents. This is useful because relying on a non-wakeup recurring alarm is not sufficient as other services make cause wakeups. It is difficult because such actions cannot be subscribed to via AndroidManifest.xml and an AppWidgetProvider is not permitted to call context.registerReceiver()
. These issues are discussed in several other StackOverflow questions including Listening for ACTION_SCREEN_OFF, android.intent.action.SCREEN_ON doesn't work as a receiver intent filter and Android - how to receive broadcast intents ACTION_SCREEN_ON/OFF?.
I have succeeded in subscribing to ACTION_SCREEN_OFF/ACTION_SCREEN_ON intents in a widget by creating a subsidiary BroascastReceiver
instance and using context.getApplicationContext().registerReceiver()
to register it. This could well be cheating and, at least in some subsequent Android release, may fail at the registration stage or perhaps the events will simply not be delivered. I have coded to handle these cases but for now it works. Unfortunately, of course, this will fail if and when the application is ever killed.
Another possibility is to use an isHomeScreenShowing()
sort of method, as described here, referenced by xandy's answer. The ideas there could probably be optimized by caching the generated list of installed CATEGORY_HOME
applications and listening to ACTION_PACKAGE_ADDED/CHANGED/REMOVED broadcasts to update it.
My strategy is:
- Try to avoid being called (via a repeating alarm) when not visible.
- When called, check screen state and other visibility indicators before doing anything expensive.
- Only then invoke an
IntentService
to handle relatively expensive work, which includes a persistent network connection. This is necessary to monitor the state of a remote service in near real time.