Skip to content

Android 电量优化

目录

  1. 引言
  2. 后台任务优化
  3. [Doze 模式影响](#doze 模式影响)
  4. App Standby
  5. [JobScheduler 使用](#jobscheduler 使用)
  6. [WorkManager 电量友好](#workmanager 电量友好)
  7. 唤醒锁优化
  8. 定位优化
  9. 网络优化
  10. 面试考点
  11. 最佳实践
  12. 总结

引言

电量优化是 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、优化唤醒锁、定位和网络请求,可以显著降低应用电量消耗。

关键要点

  1. 后台任务:使用 JobScheduler 和 WorkManager
  2. Doze 模式:适配系统限制
  3. App Standby:根据 bucket 调整行为
  4. 唤醒锁:谨慎使用,及时释放
  5. 定位优化:使用低精度和 Fused Location
  6. 网络优化:批量请求,减少重试

电量优化效果

优化项优化前优化后提升
后台电量消耗15%/天5%/天67%
定位电量消耗10%/天3%/天70%
网络电量消耗8%/天4%/天50%

通过系统的电量优化,可以显著提升用户体验,延长设备续航,获得更好的用户评价。