Live wallpapers are fun and eye-catching, but an incorrect implementation of a live wallpaper can quickly drain the battery of any powerful Android™ phone. This tutorial will explain how big this problem can be, what the reasons are and show you a code example on how you can avoid this happening to your live wallpapers.
A live wallpaper in Android™ is implemented as an Android™ service and will stay alive running in the background as long as it is the currently active wallpaper. As a developer you will have access to hardware and framework components such as the camera, GPS, compass, accelerometer and 3D graphics, just as for any Android™ services and applications. The possibility to create a live wallpaper enhanced with visual effects and features are more or less unlimited. However, it must be remembered that from a power consumption perspective the more complex drawings and hardware features you add will affect the overall performance and battery life time since the Android™ framework will make sure that the live wallpaper service is kept alive forever. This is especially critical if the live wallpaper misbehaves in the background. The unintended power consumption can then quickly drain the battery and the user will be far from happy having to recharge the phone all too often.
Two examples
Let’s compare two live wallpapers in a test scenario where the user wakes up the phone from standby, switches to a live wallpaper and then puts the phone back to standby. The graph (ampere on Y-axis, time on x-axis) to the left shows a live wallpaper that handles the resources correct and the graph to the right shows a wallpaper that keeps listening for events from the orientation sensor even when it is put back to sleep.


From the graph to the left you can see that when all resources are turned off the live wallpaper is back to consuming as much current as before the phone woke up from sleep, around 3-5 mA in average. However, in the graph to the right the live wallpaper keeps listening to events from the orientation sensor even when the phone is put to sleep and the power consumption is around 35 mA in average.
Does it really make a difference?
Yes, it does. Quite a lot actually. The graph below shows stand-by time in hours on the Y-axis and power consumption in mA during stand-by on the x-axis. As you can see the stand-by time decreases drastically even for small stand-by currents and the stand-by time drops off exponentially with increased power consumption during stand-by. From the example above, with a stand-by power consumption of 3-5 mA for one live wallpaper and 35 mA for the other, it is obvious that this difference in power consumption has a huge difference in stand-by time for the phone.
Why such big difference?
Most smartphones today has two CPUs, one main CPU where the Android™ OS along with its underlying Linux kernel and your application is running and one network CPU dedicated for all network activities. The network CPU is usually slower and thus less power consuming. In the lowest power mode, e.g. when the phone is put to sleep, Android™ is suspended and more or less only the network stack is kept alive for receiving network events about incoming calls and other essential types of network activities. This means that the power efficient network CPU is active and the main CPU is completely shut off or in a very low power state. The total power consumption will then be low.
However, if the phone is kept alive by something in the application layer that is for example constantly polling a sensor for data, this means that the entire Android™ system and main CPU will have to stay awake to provide your application with information from the sensor, information that might not be needed when the phone is in sleep mode. And as long as your application is keeping the system alive, nothing prevents other applications and services from running whatever background tasks they want as well (click here to read a blog post by my colleague Håkan Jonsson about how to deal with this). This is why it’s so important that every component in the system takes responsibility for power consumption.
How to fix it
The solution to get power efficient live wallpapers is really the same as when working with activities in regular Android™ applications. However, we cannot rely on the onDestroy() callback as we have no idea of when, or even if, it will be called. Instead we must do clean-up when we lose visibility. In activities we setup and release resources in onResume() and onPause() and in a live wallpaper service we implement the corresponding functionality in WallpaperService.Engine.OnVisibilityChanged() (click here for link to Android™ documentation). Here we must unregister receivers, listeners, suspend threads and make sure that practically everything that can keep the phone busy is stopped, drawing graphics included.
[java]
public class CompassWallpaperService extends WallpaperService {
/**
* Called every time a new Engine instance is requested. Multiple instances
* must be able to coexist, for example one instance for the active
* wallpaper and a second for the preview in the live wallpaper picker.
*/
@Override
public Engine onCreateEngine() {
return new WpEngine();
}
/**
* The Wallpaper.Engine returned by onCreateEngine().
*/
class WpEngine extends Engine {
private SensorManager mSensorManager = null;
/**
* Initializes the engine, called only once.
*/
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
}
/**
* Called right before this Engine instance is destroyed. All resources
* should have been closed and released in onVisibilityChanged, but to
* be on the safe side and to make sure we don’t leak anything we do an
* extra cleanup here as well.
*/
@Override
public void onDestroy() {
super.onDestroy();
mSensorManager.unregisterListener(mCompassListener);
}
/**
* Called when the wallpaper is becoming visible or hidden. This is
* where all CPU and power consuming tasks must stop and resources
* are closed and released. This is also the right place to start and
* stop all drawing activities.
*/
@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
mSensorManager.registerListener(mCompassListener, sensor,
SensorManager.SENSOR_DELAY_NORMAL);
} else {
mSensorManager.unregisterListener(mCompassListener);
}
}
/**
* Callbacks from the sensor.
* TODO Implement.
*/
private final SensorEventListener mCompassListener = new SensorEventListener() {
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
public void onSensorChanged(SensorEvent event) {}
};
}
}
[/java]
More Information
Sort by