Appearance
Android 电量优化
目录
- 引言
- 后台任务优化
- [Doze 模式影响](#doze 模式影响)
- App Standby
- [JobScheduler 使用](#jobscheduler 使用)
- [WorkManager 电量友好](#workmanager 电量友好)
- 唤醒锁优化
- 定位优化
- 网络优化
- 面试考点
- 最佳实践
- 总结
引言
电量优化是 Android 应用性能优化的重要组成部分。耗电量高的应用会被用户卸载,被系统限制,甚至被应用商店下架。本文将深入探讨 Android 电量优化的各个方面,提供实用的优化方案和代码示例。
为什么需要电量优化?
- 用户体验:电量消耗直接影响用户使用时长
- 系统限制:Android 系统会对耗电应用进行限制
- 用户留存:耗电是用户卸载应用的主要原因之一
- 应用评分:耗电应用容易获得低评分
电量消耗来源
┌───────────────────────────────────────────┐
│ Android 电量消耗分析 │
├───────────────────────────────────────────┤
│ 网络活动 ████████████████░░░░ 40% │
│ 屏幕显示 ██████████████░░░░░░ 35% │
│ 定位服务 ██████░░░░░░░░░░░░░░ 15% │
│ CPU 计算 ████░░░░░░░░░░░░░░░░ 10% │
└───────────────────────────────────────────┘后台任务优化
任务调度优化
kotlin
// ❌ 错误:频繁执行后台任务
class BadBackgroundService : Service() {
private val timer = Timer()
override fun onCreate() {
timer.schedule(object : TimerTask() {
override fun run() {
syncData() // 每分钟执行
}
}, 0, 60000)
}
}
// ✅ 正确:使用 JobScheduler
class SyncJobService : JobService() {
override fun onStartJob(params: JobParameters): Boolean {
syncData()
jobFinished(params, false)
return false
}
override fun onStopped(jobParams: JobParameters): Boolean {
return false
}
}后台服务优化
kotlin
// 前台服务优化
class OptimizedForegroundService : Service() {
override fun onCreate() {
super.onCreate()
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("优化中")
.setSmallIcon(R.drawable.ic_notification)
.setPriority(NotificationCompat.PRIORITY_LOW) // 降低优先级
.setOngoing(true)
.build()
startForeground(1, notification)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// 使用 START_STICKY 避免频繁重启
return START_STICKY
}
}后台任务批处理
kotlin
class BatchTaskManager {
private val taskQueue = ArrayDeque<Runnable>()
private var batchTimer: Handler? = null
private val BATCH_DELAY = 30000L // 30 秒批量执行
fun addTask(task: Runnable) {
taskQueue.add(task)
scheduleBatch()
}
private fun scheduleBatch() {
batchTimer?.removeCallbacksAndMessages(null)
batchTimer?.postDelayed({ executeBatch() }, BATCH_DELAY)
}
private fun executeBatch() {
while (taskQueue.isNotEmpty()) {
taskQueue.poll()?.invoke()
}
}
}Doze 模式影响
Doze 模式介绍
Android 6.0+ 引入了 Doze 模式,在设备闲置且充电时自动进入:
┌─────────────────────────────────────────┐
│ Doze 模式时间线 │
├─────────────────────────────────────────┤
│ 闲置 15 分钟 → 进入 Doze │
│ └── 网络访问暂停 │
│ └── 闹钟调度器暂停 │
│ └── 广播接收器暂停 │
│ │
│ 每 2 小时 → 维护窗口 │
│ └── 允许后台任务执行 │
│ └── 允许网络访问 │
└─────────────────────────────────────────┘Doze 模式适配
kotlin
class DozeAdapter {
private val context: Context
fun checkDozeMode(): Boolean {
val powerManager = context.getSystemService(POWER_SERVICE) as PowerManager
return powerManager.isDeviceIdleMode
}
fun scheduleWorkCompat(work: Runnable) {
if (checkDozeMode()) {
// Doze 模式下使用 Maintenance Window
val jobInfo = JobInfo.Builder(1, ComponentName(context, DozeJobService::class.java))
.setRequiresCharging(false)
.setPeriodic(2 * 60 * 60 * 1000) // 2 小时
.build()
val jobScheduler = context.getSystemService(JOB_SCHEDULER_SERVICE) as JobScheduler
jobScheduler.schedule(jobInfo)
} else {
// 正常模式直接执行
work.run()
}
}
}忽略 Doze 模式(特殊应用)
kotlin
// 仅对特殊应用(如健康、安全类)使用
class SpecialAppPermissionManager {
fun requestIgnoreBatteryOptimizations() {
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
data = Uri.parse("package:$packageName")
}
startActivity(intent)
}
fun checkIgnoreBatteryOptimizations(): Boolean {
val powerManager = context.getSystemService(POWER_SERVICE) as PowerManager
return powerManager.isIgnoringBatteryOptimizations(context.packageName)
}
}App Standby
App Standby bucket 分类
┌─────────────────────────────────────────┐
│ App Standby Bucket 分类 │
├─────────────────────────────────────────┤
│ Active 用户正在使用的应用 │
│ Frequent 用户频繁使用的应用 │
│ Rare 用户使用较少的应用 │
│ Work 工作相关的应用 │
│ Device 设备管理类应用 │
└─────────────────────────────────────────┘App Standby 适配策略
kotlin
class AppStandbyAdapter {
fun getBucketForApp(packageName: String): Int {
val cmd = arrayOf("dumpsys", "usage", packageName)
val process = Runtime.getRuntime().exec(cmd)
val output = process.inputStream.bufferedReader().readText()
return when {
output.contains("ACTIVE") -> UsageStatsManager.BUCKET_ACTIVE
output.contains("FREQUENT") -> UsageStatsManager.BUCKET_FREQUENT
output.contains("RARE") -> UsageStatsManager.BUCKET_RARE
else -> UsageStatsManager.BUCKET_UNKNOWN
}
}
// 根据 bucket 调整行为
fun adaptToBucket(bucket: Int) {
when (bucket) {
UsageStatsManager.BUCKET_ACTIVE -> {
// 正常使用
scheduleFrequentSync()
}
UsageStatsManager.BUCKET_RARE -> {
// 减少同步频率
scheduleRareSync()
}
}
}
}前台服务豁免
kotlin
class ForegroundServiceExemption {
// 前台服务可以豁免 App Standby 限制
class ExemptForegroundService : Service() {
override fun onCreate() {
super.onCreate()
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("后台运行")
.setSmallIcon(R.drawable.ic_notification)
.setForegroundServiceType(
ForegroundServiceType.LOCATION
) // 声明服务类型
.build()
startForeground(1, notification)
}
}
}JobScheduler 使用
JobScheduler 配置
kotlin
class JobSchedulerManager {
private lateinit var jobScheduler: JobScheduler
fun initialize(context: Context) {
jobScheduler = context.getSystemService(JOB_SCHEDULER_SERVICE) as JobScheduler
}
// 配置后台任务
fun scheduleSyncJob() {
val component = ComponentName(context, SyncJobService::class.java)
val builder = JobInfo.Builder(1, component)
.setRequiresCharging(false) // 不需要充电
.setRequiresDeviceIdle(false) // 不需要设备闲置
.setRequiresWifi(true) // 需要 WiFi
.setPersisted(false) // 不持久化
.setPeriodic(60 * 60 * 1000) // 每小时执行
.setBackoffCriteria(15 * 60 * 1000, JobInfo.BACKOFF_EXPONENTIAL)
jobScheduler.schedule(builder.build())
}
// 取消任务
fun cancelSyncJob() {
jobScheduler.cancel(1)
}
}JobService 实现
kotlin
class SyncJobService : JobService() {
override fun onStartJob(params: JobParameters): Boolean {
Log.d("JobService", "Starting job ${params.jobId}")
// 执行后台任务
performSync()
// 任务完成,返回 false 表示不需要重试
jobFinished(params, false)
return false
}
override fun onStopped(params: JobParameters): Boolean {
Log.d("JobService", "Job stopped ${params.jobId}")
return false
}
override fun onTerminate() {
super.onTerminate()
// 清理资源
}
private fun performSync() {
// 同步数据
// 使用电量友好的方式
}
}
// AndroidManifest.xml
<service
android:name=".SyncJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" />任务调度策略
kotlin
class TaskSchedulerStrategy {
enum class PowerMode {
BATTERY_SAVINGS, // 省电模式
NORMAL, // 正常模式
CHARGING // 充电中
}
private var currentMode = PowerMode.NORMAL
fun scheduleTask(task: JobInfo.Builder, mode: PowerMode) {
when (mode) {
PowerMode.BATTERY_SAVINGS -> {
task
.setRequiresCharging(true)
.setRequiresDeviceIdle(true)
.setPeriodic(4 * 60 * 60 * 1000) // 4 小时
}
PowerMode.CHARGING -> {
task
.setRequiresCharging(true)
.setPeriodic(1 * 60 * 60 * 1000) // 1 小时
}
PowerMode.NORMAL -> {
task
.setRequiresWifi(true)
.setPeriodic(2 * 60 * 60 * 1000) // 2 小时
}
}
}
}WorkManager 电量友好
WorkManager 基础使用
kotlin
class WorkManagerExample {
// 一次性任务
fun enqueueOneTimeWork() {
val workRequest = OneTimeWorkRequestBuilder<DataWorker>().build()
WorkManager.getInstance(context).enqueue(workRequest)
}
// 周期性任务
fun enqueuePeriodicWork() {
val workRequest = PeriodicWorkRequestBuilder<DataWorker>(
1, TimeUnit.HOURS // 最小间隔 15 分钟
).build()
WorkManager.getInstance(context).enqueue(workRequest)
}
}电量友好的 Work 配置
kotlin
class PowerAwareWorkConfiguration {
fun createBatteryFriendlyWork(): WorkRequest {
return PeriodicWorkRequestBuilder<SyncWorker>(
24, TimeUnit.HOURS
)
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.NOT_ROAMING) // 非漫游网络
.setRequiresCharging(false) // 不要求充电
.setRequiresBatteryNotLow(true) // 电量充足
.setRequiresDeviceIdle(true) // 设备闲置
.build()
)
.build()
}
}Work 链式执行
kotlin
class WorkChainExample {
fun enqueueWorkChain() {
val work1 = OneTimeWorkRequestBuilder<FetchWorker>().build()
val work2 = OneTimeWorkRequestBuilder<ProcessWorker>().build()
val work3 = OneTimeWorkRequestBuilder<UploadWorker>().build()
WorkManager.getInstance(context)
.beginWork()
.then(work1)
.then(work2)
.then(work3)
.enqueue()
}
}Worker 实现
kotlin
class SyncWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
override fun doWork(): Result {
return try {
// 执行同步任务
performSync()
Result.success()
} catch (e: Exception) {
// 失败重试
Result.retry()
}
}
private fun performSync() {
// 使用电量友好的方式同步数据
// 批量操作,减少网络请求
}
}唤醒锁优化
唤醒锁类型
┌─────────────────────────────────────────┐
│ 唤醒锁类型 │
├─────────────────────────────────────────┤
│ PARTIAL_WAKE_LOCK 保持 CPU 运行 │
│ FULL_WAKE_LOCK 保持 CPU 和屏幕 │
│ SCREEN_DIM_WAKE_LOCK 保持 CPU 和屏幕变暗│
└─────────────────────────────────────────┘唤醒锁最佳实践
kotlin
// ❌ 错误:长时间持有唤醒锁
class BadWakeLock {
private var wakeLock: PowerManager.WakeLock? = null
init {
val powerManager = context.getSystemService(POWER_SERVICE) as PowerManager
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "tag")
wakeLock?.acquire(10 * 60 * 1000) // 持有 10 分钟!
}
}
// ✅ 正确:短暂持有并释放
class GoodWakeLock {
private var wakeLock: PowerManager.WakeLock? = null
fun performTask() {
val powerManager = context.getSystemService(POWER_SERVICE) as PowerManager
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "tag")
wakeLock?.acquire(5 * 60 * 1000) // 最多 5 分钟
try {
// 执行任务
doTask()
} finally {
wakeLock?.release() // 务必释放
}
}
}同步唤醒锁
kotlin
class SyncWakeLock {
private var wakeLock: PowerManager.WakeLock? = null
fun acquireSyncWakeLock(): PowerManager.WakeLock {
val powerManager = context.getSystemService(POWER_SERVICE) as PowerManager
val wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"com.example::sync"
).apply {
referenceCounted = false // 非引用计数
acquire(30 * 60 * 1000) // 30 分钟
}
return wakeLock
}
}定位优化
定位精度优化
kotlin
class LocationOptimizer {
private val locationManager = context.getSystemService(LOCATION_SERVICE) as LocationManager
// 低精度定位(省电)
fun requestLowPowerLocation(): Location {
return try {
// 使用网络定位
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
10 * 60 * 1000, // 10 分钟
0f, // 不要求距离变化
locationListener
)
} catch (e: SecurityException) {
// 处理权限
}
}
// 高精度定位(耗电)
fun requestHighAccuracyLocation(): Location {
return try {
// 使用 GPS
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
1000, // 1 秒
10f, // 10 米
locationListener
)
} catch (e: SecurityException) {
// 处理权限
}
}
// 使用 Fused Location Provider(推荐)
fun requestFusedLocation(): Location {
val locationRequest = LocationRequest.Builder(
LocationRequest.PRIORITY_LOW_POWER // 低功耗优先
).apply {
setUpdateInterval(10 * 60 * 1000) // 10 分钟
setFastestUpdateInterval(5 * 60 * 1000) // 5 分钟
}.build()
LocationServices.getFusedLocationProviderClient(context)
.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
}
}定位频率优化
kotlin
class LocationFrequencyOptimizer {
private var lastLocation: Location? = null
private val minDistance = 1000f // 1 公里
fun shouldUpdateLocation(newLocation: Location): Boolean {
lastLocation?.let { last ->
val distance = last.distanceTo(newLocation)
if (distance < minDistance) {
return false // 距离变化不够,不更新
}
}
lastLocation = newLocation
return true
}
}前台定位服务
kotlin
class LocationForegroundService : Service() {
private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
override fun onCreate() {
super.onCreate()
val locationRequest = LocationRequest.Builder(
LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
).apply {
setUpdateInterval(60 * 1000) // 1 分钟
}.build()
val notification = createNotification()
startForeground(1, notification)
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
}
}网络优化
网络请求电量优化
kotlin
class NetworkPowerOptimizer {
// 批量请求
fun batchRequest(urls: List<String>) {
// 合并多个请求为一个
val combinedRequest = createCombinedRequest(urls)
executeRequest(combinedRequest)
}
// 使用连接池
private val client = OkHttpClient.Builder()
.connectionPool(ConnectionPool(10, 5, TimeUnit.MINUTES))
.build()
// 减少重传
class RetryInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
// 实现指数退避重试
}
}
}网络状态监听
kotlin
class NetworkStateListener {
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
// 网络可用时再执行请求
}
override fun onLost(network: Network) {
// 网络断开时暂停请求
}
}
fun start() {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.registerDefaultNetworkCallback(networkCallback)
}
}面试考点
基础考点
1. 唤醒锁的作用
问题: 什么是唤醒锁?为什么要谨慎使用?
回答:
- 唤醒锁用于保持 CPU 或屏幕运行
- 过度使用会严重影响电量
- 应尽可能使用同步唤醒锁而非异步
- Android 6.0+ 推荐使用 JobScheduler 和 WorkManager
2. Doze 模式
问题: Doze 模式对应用有什么影响?
回答:
- 暂停网络访问
- 暂停闹钟调度器
- 暂停广播接收器
- 每 2 小时允许维护窗口
3. 定位耗电
问题: 为什么 GPS 定位耗电?如何优化?
回答:
- GPS 需要持续使用硬件传感器
- 使用 Fused Location Provider
- 降低定位频率
- 使用低精度定位(网络定位)
进阶考点
1. WorkManager 电量优化
问题: WorkManager 如何实现电量友好?
回答:
- 自动合并任务
- 考虑 Doze 模式
- 支持约束条件(充电、WiFi)
- 支持任务链式执行
2. App Standby
问题: App Standby 如何影响应用?
回答:
- 根据使用情况分类应用
- 限制后台任务和广播
- 网络访问受限
- 前台服务可豁免
高级考点
1. 电池优化豁免
问题: 如何合理请求电池优化豁免?
回答:
- 仅对必要应用请求
- 在应用首次启动后请求
- 提供清晰的用途说明
- 考虑使用前台服务替代
2. 后台执行限制
问题: Android 后台执行限制有哪些?
回答:
- Android 8.0 引入后台启动限制
- 隐式广播受限
- 后台服务启动限制
- 使用 JobScheduler 和 WorkManager 替代
最佳实践
电量监控
kotlin
class BatteryMonitor {
private val batteryReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
val scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100)
val percentage = level * 100 / scale
if (percentage < 20) {
// 低电量优化
enableLowPowerMode()
}
}
}
fun startMonitoring() {
context.registerReceiver(
batteryReceiver,
IntentFilter(Intent.ACTION_BATTERY_CHANGED)
)
}
}常见错误
1. 未释放唤醒锁
kotlin
// ❌ 错误:未释放唤醒锁
val wakeLock = powerManager.newWakeLock(PARTIAL_WAKE_LOCK, "tag")
wakeLock.acquire()
// 忘记释放!
// ✅ 正确:使用 try-finally
val wakeLock = powerManager.newWakeLock(PARTIAL_WAKE_LOCK, "tag")
wakeLock.acquire(5 * 60 * 1000)
try {
doTask()
} finally {
wakeLock.release()
}2. 频繁定位
kotlin
// ❌ 错误:1 秒更新一次 GPS
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
1000, // 1 秒
0f,
listener
)
// ✅ 正确:使用 Fused Location Provider
val request = LocationRequest.Builder(PRIORITY_LOW_POWER)
.setUpdateInterval(10 * 60 * 1000) // 10 分钟
.build()总结
电量优化是 Android 应用优化中不可忽视的一环。通过合理管理后台任务、适配 Doze 模式和使用 App Standby、使用 JobScheduler 和 WorkManager、优化唤醒锁、定位和网络请求,可以显著降低应用电量消耗。
关键要点
- 后台任务:使用 JobScheduler 和 WorkManager
- Doze 模式:适配系统限制
- App Standby:根据 bucket 调整行为
- 唤醒锁:谨慎使用,及时释放
- 定位优化:使用低精度和 Fused Location
- 网络优化:批量请求,减少重试
电量优化效果
| 优化项 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 后台电量消耗 | 15%/天 | 5%/天 | 67% |
| 定位电量消耗 | 10%/天 | 3%/天 | 70% |
| 网络电量消耗 | 8%/天 | 4%/天 | 50% |
通过系统的电量优化,可以显著提升用户体验,延长设备续航,获得更好的用户评价。