Batteries

Different devices have different capacities. Battery capacity for phones and tablets is often measured in mAh—that is, milliampere-hour. Table 7–1 shows the capacities of the devices mentioned in Chapter 2.

NOTE: The ampere, named after André-Marie Ampère, is an SI unit of electric current and is often shortened to “amp.” One ampere-hour equals 3,600 coulombs, and therefore one ampere-second equals one coulomb, and one mAh equals 3.6 coulombs. The coulomb, an SI unit named after Charles-Augustin de Coulomb, is rarely used in the descriptions of consumer products.

Image

The fact that tablets use batteries with much larger capacities is a strong indicator that the screen alone consumes a lot of power. Android provides a way for the user to know approximately how much power is used by applications and system components. Figure 7–1 shows how much power was used on a Galaxy Tab 10.1 while spending most of the time using a slingshot to throw choleric feathered animals at swine.

Image

Figure 7–1. Battery usage

Two items clearly stand out in this screenshot: Screen and Wi-Fi. As these two components use a lot of power, devices provide ways for end-users to configure their usage. For example, users can change the brightness of the screen (manually or automatically based on the image displayed), define after how much time without activity the screen should turn off, and also have Wi-Fi turned off whenever the screen turns off. For instance, the Wi-Fi connection may represent only a few percent of the total battery usage when it is turned off as soon as the screen turns off.

NOTE: The Galaxy Tab 10.1 used here is a Wi-Fi-only version. Other items will show with different devices, for example “Cell standby” or “Voice calls.”

Although users themselves can proactively manage the battery usage, this is not without its own limitations. Ultimately, how much power is used on a device is heavily dependent on what all the applications do, and therefore dependent on how you designed and implemented your application.

Typical things your applications do are:

  • Executing code (Captain Obvious would not have said it better)
  • Transferring data (downloading and uploading, using Wi-Fi, EDGE, 3G, 4G)
  • Tracking location (using network or GPS)
  • Using sensors (accelerometer, gyroscope)
  • Rendering images (using GPU or CPU)
  • Waking up to perform various tasks

Before we learn how to minimize the battery usage, we should have a way to measure how much power the application uses.

Measuring Battery Usage

Unfortunately, such accurate measurements require electrical equipment most developers don't have access to. However, Android provides APIs to get information about the battery usage. While there is no API such as getBatteryInfo(), it is possible to retrieve the battery information via a so-called sticky intent, that is, a broadcast intent that is always around, as shown in Listing 7–1.

Listing 7–1. Activity Showing Battery Information

import static android.os.BatteryManager.*;
// note the static keyword here (don't know what it does? Remove it and see!)

public class BatteryInfoActivity extends Activity {
    private static final String TAG = "BatteryInfo";

    private BroadcastReceiver mBatteryChangedReceiver;
    private TextView mTextView; // layout contains TextView to show battery information

    private static String healthCodeToString(int health) {
        switch (health) {
        //case BATTERY_HEALTH_COLD: return "Cold"; // API level 11 only
        case BATTERY_HEALTH_DEAD: return "Dead";
        case BATTERY_HEALTH_GOOD: return "Good";
        caseBATTERY_HEALTH_OVERHEAT: return "Overheat";
        case BATTERY_HEALTH_OVER_VOLTAGE: return "Over voltage";
        case BATTERY_HEALTH_UNSPECIFIED_FAILURE: return "Unspecified failure";
        case BATTERY_HEALTH_UNKNOWN:
        default: return "Unknown";
        }
    }

    private static String pluggedCodeToString(int plugged) {
        switch (plugged) {
        case 0: return "Battery";
        case BATTERY_PLUGGED_AC: return "AC";
        case BATTERY_PLUGGED_USB: return "USB";
        default: return "Unknown";
        }
    }

    private static String statusCodeToString(int status) {
        switch (status) {
        case BATTERY_STATUS_CHARGING: return "Charging";
        case BATTERY_STATUS_DISCHARGING: return "Discharging";
        case BATTERY_STATUS_FULL: return "Full";
        case BATTERY_STATUS_NOT_CHARGING: return "Not charging";
        case BATTERY_STATUS_UNKNOWN:
        default: return "Unknown";
        }
    }

    private void showBatteryInfo(Intent intent) {
        if (intent != null) {
            int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
            String healthString = "Health: " + healthCodeToString(health);
            Log.i(TAG, healthString);

            int level = intent.getIntExtra(EXTRA_LEVEL, 0);
            int scale = intent.getIntExtra(EXTRA_SCALE, 100);
            float percentage = (scale != 0) ? (100.f * (level / (float)scale)) : 0.0f;
            String levelString = String.format("Level: %d/%d (%.2f%%)", level, scale,
percentage);
            Log.i(TAG, levelString);

            int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
            String pluggedString = "Power source: " + pluggedCodeToString(plugged);
            Log.i(TAG, pluggedString);

            boolean present = intent.getBooleanExtra(EXTRA_PRESENT, false);
            String presentString = "Present? " + (present ? "Yes" : "No");
            Log.i(TAG, presentString);

            int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
            String statusString = "Status: " + statusCodeToString(status);
            Log.i(TAG, statusString);

            String technology = intent.getStringExtra(EXTRA_TECHNOLOGY);
            String technologyString = "Technology: " + technology;
            Log.i(TAG, technologyString);

            int temperature = intent.getIntExtra(EXTRA_STATUS, Integer.MIN_VALUE);
            String temperatureString = "Temperature: " + temperature;
            Log.i(TAG, temperatureString);

            int voltage = intent.getIntExtra(EXTRA_VOLTAGE, Integer.MIN_VALUE);
            String voltageString = "Voltage: " + voltage;
            Log.i(TAG, voltageString);

            String s = healthString + " ";
            s += levelString + " ";
            s += pluggedString + " ";
            s += presentString + " ";
            s += statusString + " ";
            s += technologyString + " ";
            s += temperatureString + " ";
            s += voltageString;
            mTextView.setText(s);

            // Note: using a StringBuilder object would have been more efficient

            int id = intent.getIntExtra(EXTRA_ICON_SMALL, 0);
            setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, id);
        } else {
            String s = "No battery information";
            Log.i(TAG, s);
            mTextView.setText(s);

            setFeatureDrawable(Window.FEATURE_LEFT_ICON, null);

        }
    }

    private void showBatteryInfo() {
        // no receiver needed
        Intent intent = registerReceiver(null, new
IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        showBatteryInfo(intent);
    }

    private void createBatteryReceiver() {
        mBatteryChangedReceiver = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                showBatteryInfo(intent);
            }
        };
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_LEFT_ICON);
        setContentView(R.layout.main);

        mTextView = (TextView) findViewById(R.id.battery);

        showBatteryInfo(); // no receiver needed
    }

    @Override
    protected void onPause() {
        super.onPause();

        // unregistering the receiver when the application is not in the foreground
saves power
        unregisterReceiver(mBatteryChangedReceiver);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mBatteryChangedReceiver == null) {
            createBatteryReceiver();
        }
        registerReceiver(mBatteryChangedReceiver,
            new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        unregisterReceiver(mBatteryChangedReceiver);
        mBatteryChangedReceiver= null;
    }
}

As you can see, the battery information is part of the intent's extra information. This activity wants to be notified of changes therefore it registers a broadcast receiver in onResume(). However, since the sole purpose of the notification is to update the user interface with the new battery information, the activity needs to be notified only when it is in the foreground and when the user is directly interacting with the application, and consequently it unregisters the broadcast receiver in onPause().

NOTE: Another possible implementation is to move the registration and un-registration of the receiver to onStart() and onStop() respectively. To achieve greater power savings, it is usually better to register and unregister broadcast receivers in onResume() and onPause() though.

If you need to know the current battery information but do not need to be notified of changes, you can simply get the sticky intent containing the battery information without registering any broadcast receiver by calling registerReceiver() and passing null as the broadcast receiver.

To measure the battery usage, it is recommended you get the battery level when your application starts, use your application for a while, and then when the application exits once again get the battery level. While the difference between the two levels won't tell you exactly how much power your own application uses (as other applications can still be running at the same time), it should give you a good idea of your application's power usage. For example, you could determine how much time one could use your application before the battery is empty.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.238.161