Keywords: Android | Battery Level | Battery State | Broadcast Receiver | BatteryManager | Java | Kotlin | Version Compatibility
Abstract: This article explores multiple methods for retrieving battery level and state in Android applications, including using broadcast receivers to dynamically listen for ACTION_BATTERY_CHANGED intents and leveraging modern APIs from the BatteryManager class. Based on best practices, it provides Java and Kotlin code examples and addresses compatibility issues across different Android versions, aiming to help developers efficiently manage device power states.
Introduction
In Android development, monitoring device battery level and state is crucial for optimizing user experience and energy efficiency. Developers often need to access battery information, such as percentage charge and charging status (e.g., charging, discharging, or plugged in), to adjust application behavior or provide relevant notifications. However, the Android system offers various approaches to achieve this, primarily involving the BatteryManager class and broadcast receivers.
Using Broadcast Receivers to Get Battery Information
A traditional and widely used method involves dynamically registering a broadcast receiver to listen for the system-broadcast ACTION_BATTERY_CHANGED intent. Since this intent cannot be received via components declared in manifests, it must be explicitly registered at runtime using Context.registerReceiver(). The following is a basic implementation example demonstrating how to set up a broadcast receiver in an Activity and calculate battery percentage.
public class MainActivity extends Activity {
private TextView batteryTextView;
private BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
if (level != -1 && scale != -1) {
float batteryPercentage = level * 100 / (float) scale;
batteryTextView.setText(String.valueOf(batteryPercentage) + "%");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
batteryTextView = findViewById(R.id.batteryTxt);
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(batteryReceiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(batteryReceiver);
}
}
This method directly handles extras from the intent, such as EXTRA_LEVEL and EXTRA_SCALE, to compute battery percentage without relying on specific Android versions.
Using Modern Methods with BatteryManager
Starting from Android 5.0 (LOLLIPOP, SDK 21), the BatteryManager class introduced more convenient APIs that allow developers to directly retrieve battery properties. For example, the getIntProperty() method can be used to easily obtain battery capacity as a percentage. The following code snippet illustrates how to use this method in Android 5.0 and above.
public static int getBatteryPercentage(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
return -1; // Handle older versions
}
}
This approach simplifies code by avoiding the complexity of dynamic broadcast receivers but is limited to newer system versions.
Handling Version Compatibility
To ensure application compatibility across a wide range of Android devices, developers need to combine the above methods. A comprehensive solution includes conditional logic to select the optimal method based on the device's system version. Below is an example of a Helper class providing Java and Kotlin implementations to retrieve battery percentage while supporting all Android versions.
// Java version
public class BatteryUtils {
public static int getBatteryPercentage(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
return bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, filter);
if (batteryStatus != null) {
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
if (level != -1 && scale != -1) {
double batteryPct = level / (double) scale;
return (int) (batteryPct * 100);
}
}
return -1; // Indicates failure to retrieve
}
}
}
// Kotlin version
fun getBatteryPercentage(context: Context): Int {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val bm = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
val batteryStatus: Intent? = context.registerReceiver(null, filter)
val level = batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
val scale = batteryStatus?.getIntExtra(BatteryManager.EXTRA_SCALE, -1) ?: -1
if (level != -1 && scale != -1) {
val batteryPct = level / scale.toDouble()
(batteryPct * 100).toInt()
} else {
-1
}
}
}
This implementation prioritizes modern APIs for efficiency while falling back to the broadcast receiver method for older versions.
Conclusion
Retrieving battery level and state in Android devices is a common requirement in development. By combining broadcast receivers with BatteryManager APIs, developers can build robust and compatible solutions. It is recommended to adopt conditional logic in applications to handle different Android versions, ensuring optimal performance and user experience. Future updates to Android may introduce additional APIs, but current methods cover mainstream devices from older to newer versions.