Battery life

Being conservative with power consumption and having a great user experience aren't mutually exclusive concepts. You might notice that when you are conservative with power usage that your users are more satisfied with your app.

Stop animations when they're not needed

Although graphics and animations are important to the look and feel of most applications, if they're overused, they can affect the life of the device battery. Generally, you should always stop graphics and animations when they're no longer needed. When a Cascades animation runs (either implicitly or explicitly), the rendering thread redraws the scene at a rate of about 60 FPS. A significant amount of processing power is being used when the app could be inactive.

For example, consider an app that frequently requests remote data, or performs intensive operations, such as running SQL queries. Although it's important to provide a visual cue indicating that this process is underway, you need to consider how often the animation runs (whether it's an ActivityIndicator, ProgressIndicator, or your own animation). If it turns out that the animation is running constantly, you might want to reconsider how you present this information to your users.

In most cases, you can replace an animation with a static image that the user recognizes and associates with a particular process. For example, in a mapping app, an image of a compass rose or an arrow might indicate that the app is receiving location updates.

Cascades stops rendering visual updates when the target node is no longer visible on the screen. This includes instances where the app moves to the background, and (most) instances where the node becomes hidden by another node (for example, if a Page is pushed on top of a Page that has a running animation). However, it's still a good idea to stop animations manually instead of relying on the framework to know when to stop rendering visual updates.

For more information about graphics, see Graphics & Multimedia.

For more information about Cascades animations, see Animations.

Use moderation when polling sensors

Retrieving sensor readings from the device's hardware can be costly to battery life when your app receives updates too frequently. Despite the impact on battery life, receiving sensor updates at short intervals is necessary for many apps and games. For example, in a racing game that allows the user to steer the vehicle by rotating the device, you probably need to receive a steady stream of accelerometer updates when the app is running.

An app receives sensor updates in the same way regardless of the type of sensor. To capture updates for Qt sensors, you create an instance of the QSensor subclass, you set the active property to true, and you connect to the readingChanged() signal. To capture updates for BPS sensors, you first initialize BPS by calling bps_initialize() and then start receiving sensor events by calling sensor_request_events(). For some sensors, such as the holster, orientation, and proximity sensors, this approach is efficient. Because these sensors have a small set of default values, the number of updates that are reported to the app are small (for example, the holster sensor returns only a value of true or false). This situation also makes the sensors straightforward to manage because you don't need to worry about polling the sensors yourself.

This approach doesn't work well when you are accessing sensors that are able to monitor small changes in the physical environment, such as the accelerometer, gyroscope, and magnetometer sensors. These sensors can potentially send numerous updates.

There are some ways that you can reduce the number of updates that are sent to the app when you work with these sensors. For Cascades, you can use the QSensor::skipDuplicates property, which, when it's set to true, omits and skips successive readings with the same or similar values. For a C app, you can configure the sensor by calling sensor_set_rate() and sensor_set_skip_duplicates(), depending on your app's needs. You can also use a sensor filter to specify the frequency or number of new readings that the sensor delivers to your app, instead of having each new reading delivered.

For more information about how to access sensors efficiently, see Sensor efficiency.

Be conservative with location requests

Unless your app needs to know the device's precise geographic coordinates at all times (such as in a mapping or navigation app), you should be conservative with how often you send location requests.

For example, consider a social networking app that tags updates with a user's current location. Or, consider a restaurant finder app that is designed to search for restaurants near the user's current location. In these instances, retrieving the location once when it's required is probably sufficient. Although it might seem useful to have the user's precise location always available, this approach can cause a significant drain on the device battery.

For information about how you can retrieve the location of the device, see Retrieving a single fix and Retrieving multiple fixes.

Stop background processes

Many apps depend on direct interaction from the user, so that all of their processing can be done while the app is open and being used. Apps that run in the background are generally those apps that need the ability to listen for a particular event and notify the user of the event, even while the app isn't in use.

Depending on the configuration of the app, running in the background can mean different things. All apps are considered to be running in the background while they're visible as an Active Frame (also called an app cover) on the home screen. Even though an app can continue running processes in this state, it should stop any extraneous processing and use only the resources it needs to update the app cover.

To listen for this event, you can connect a slot to the bb::Application::thumbnail() signal or process the NAVIGATOR_WINDOW_THUMBNAIL navigator event to notify the app of the change in the window state and the app is placed into the NAVIGATOR_APP_BACKGROUND run partition.

When the user opens another app or the backlight turns off, apps that are running as Active Frames automatically enter the Stopped state, at which point they can't perform any operations. This approach helps to minimize battery power consumption and maximize the performance of the device.

However, apps that request the run_when_backgrounded permission continue to run in the background, even after the user opens another app or the backlight turns off. If your app does require this permission, you should still make sure to stop any operations that aren't essential. To listen for this event, you can connect a slot to the bb::Application::invisible() signal. The implementation is similar to the example above.

For more information about app states and running apps in the background, see App life cycle.

Update Active Frames efficiently

Active Frames (also known as app covers) provide your users with information without requiring them to reopen your app. An app cover can be updated dynamically at any time, but you should always be conservative with the frequency that you make updates.

The image to the right shows a few examples of app covers used in the BlackBerry 10 OS. Even though these app covers contain dynamic content (BBM displays recent contact updates and the Calendar app displays upcoming events), the content remains static after it's displayed.

Screen showing an Active Frame example.

The following code samples show different implementations of an Active Frame. One sample updates the Active Frame inefficiently and the other uses the thumbnail() signal to update the Active Frame.

Not recommended

The code sample below is a basic implementation of an app cover that displays a single integer. After the app cover is created, an update() function is called, which increments the integer and starts a timer to call the function again after 4 seconds have elapsed.

#include "ActiveFrameInefficient.h"

#include <bb/cascades/SceneCover>
#include <bb/cascades/Container>
#include <bb/cascades/Application>

ActiveFrameInefficient::ActiveFrameInefficient(QObject *parent)
	: SceneCover(parent)
{
	Container *root = new Container();
	m_theLabel = Label::create().text("0");
	root->add(m_theLabel);
	setContent(root);

	update();
}

void ActiveFrameInefficient::update() {

	QTimer::singleShot(4000, this, SLOT(update()));

	int labelNum = m_theLabel->text().toInt() + 1;
	m_theLabel->setText(QString::number(labelNum));
}

There are a couple of issues with this approach. The app cover should never need to be updated with this frequency. If updates are necessary, a frequency of every 30 seconds is probably sufficient. Another issue is that updates continue to occur even while the app is running in the foreground. Even though the visual updates aren't rendered, the app still increments the number every 4 seconds. In this example, the updates don't seem too costly, but with more elaborate app covers these updates could consume a significant amount of processing power.

Recommended

The code sample below shows you how to implement your app covers. Instead of calling the update() function when the app cover is created, you can connect to the thumbnail() signal so that updates occur only when the app becomes an Active Frame. When the app moves to the foreground, a Boolean variable called isActiveFrame is set to false, letting the app know that it can stop updating the app cover.

#include "ActiveFrame.h"

#include <bb/cascades/SceneCover>
#include <bb/cascades/Container>
#include <bb/cascades/Application>

ActiveFrame::ActiveFrame(QObject *parent)
	: SceneCover(parent)
	, isActiveFrame(false)
{
	Container *root = new Container();
	m_theLabel = Label::create().text("0");
	root->add(m_theLabel);
	setContent(root);

    bool connectResult;
    Q_UNUSED(connectResult);
    
    connectResult = QObject::connect
    (Application::instance(), 
     SIGNAL(thumbnail()), 
     this, 
     SLOT(backgrounded()));
    Q_ASSERT(connectResult);
                                     
    connectResult = QObject::connect
   (Application::instance(), 
    SIGNAL(fullscreen()), 
    this, 
    SLOT(foregrounded()));

    Q_ASSERT(connectResult);
}

void ActiveFrame::foregrounded() {
	isActiveFrame = false;
}

void ActiveFrame::backgrounded() {
	isActiveFrame = true;
	update();
}

void ActiveFrame::update() {

	if (isActiveFrame) {
		QTimer::singleShot(3000, this, SLOT(update()));
		qDebug() << "Cover updated!";

		int labelNum = m_theLabel->text().toInt() + 1;
		m_theLabel->setText(QString::number(labelNum));
	}
}

For more information about creating app covers, see Active Frames.

Push content to your app

If your app relies on updates from a server-side app, you have probably thought about how to design the app to keep data current for the user. One approach is to use a headless app to retrieve data in the background and notify the client app when new data arrives. In this scenario, the headless app performs scheduled network requests to pull the data from a remote server. The problem with this approach is that scheduled queries require processing power and network access, and the headless app might make numerous requests even when the server has no data to provide.

A headless app is an app that has been created to run in the background and doesn't have a UI from which it can accept input.

The preferred approach for receiving remote data is to use the Push Service. Push technology allows a server-side app to notify a client app when new data is available by pushing a notification with a small payload to the device. You can conserve processor power and network usage because the client app sends requests only when it knows that there's data available. The result is an app that not only conserves battery power, but also has the added benefit of near real-time updates.

For more information, see Push Service.

Reduce wakeups during standby mode

To reduce power consumption, you can decrease the number of times that a device wakes up when it is in standby mode. A device is in standby mode when it has been put to sleep or has timed out due to inactivity, and has a blank screen. During standby mode, the device tries to enter lower power states, which it can enter only when there is no activity.

A wakeup is an interrupt event that causes the kernel to schedule a thread to do some work. Wakeups can be caused by misbehaving apps or by hardware interrupts. Even when apps are in standby mode, they need to receive push events from the network or poll network services for new content. As a developer, you need to ensure that your app is not waking up unnecessarily while the device is asleep.

The standby monitor runs in the background on all BlackBerry 10 devices. The standby monitor watches for misbehaving apps and writes to a log file when it detects them. To determine whether apps are misbehaving, the standby monitor considers wakeups and CPU sleep states. Typically, an app is misbehaving when it causes wakeups more frequently than once per minute while in standby mode.

You don't need to include any libraries to use the standby monitor. On the device, the standby monitor writes to a log file under /accounts/1000/shared/misc/standby.log. In the Momentics IDE for BlackBerry, you can use the Target File System Navigator to locate and open the log file.

For more information, see Check the standby monitor log file.

Last modified: 2015-04-30



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus