引言
你有没有遇到过这样的场景:用户投诉说「你们的App太耗电了,半天就把我手机电用完了」,或者「网络不好的时候你们的App根本用不了」。作为开发者,这两个问题可能是用户体验杀手中最致命的------电量和网络。
电量优化和网络优化看似是两个独立的主题,但它们有着密切的关系:网络请求是移动设备最大的电量消耗源之一。一个糟糕的网络策略不仅让用户体验变差,还会疯狂吃掉手机电量。
今天这篇文章,我会带你深入Android的电量管理机制,教你用Battery Historian这个神器分析电量消耗,理解Doze模式和Wakelock的优化技巧,并分享弱网环境下的实战优化方案。读完这篇文章,你将掌握:
- Android电量管理的核心机制(Doze、App Standby)
- Battery Historian工具的完整使用流程
- Wakelock滥用检测和优化方法
- 弱网环境识别和优化策略
- 网络请求的最佳实践
准备好了吗?让我们开始这场「省电又流畅」的优化之旅!
Android电量管理机制
电量消耗的主要来源
在优化电量之前,我们先要知道电都去哪儿了。Android设备的电量消耗主要来自:

对于应用开发者来说,我们能优化的主要是网络通信、CPU运算、定位服务 这三大块。而网络优化又是重中之重,因为一次网络请求会同时消耗网络模块和CPU的电量。
PowerManager与电源管理服务
Android的电源管理由PowerManager系统服务负责。它提供了多种电源状态:
kotlin
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
// 检查设备是否处于省电模式
val isPowerSaveMode = powerManager.isPowerSaveMode
// 检查是否处于Doze模式
val isDeviceIdleMode = powerManager.isDeviceIdleMode
// 检查应用是否被限制后台活动
val isIgnoringBatteryOptimizations = powerManager.isIgnoringBatteryOptimizations(packageName)
Doze模式:深度睡眠的艺术
Doze模式是Android 6.0引入的重磅功能,目的是在设备长时间静置时进入深度睡眠,极大降低电量消耗。
Doze模式触发条件
设备必须同时满足以下条件才会进入Doze:
- 屏幕关闭
- 设备未在充电
- 设备静置(没有移动)
- 一段时间没有使用(通常是1-2小时)

Doze模式的限制
一旦进入Doze,系统会施加以下限制:
- 网络访问暂停 - 除了高优先级FCM消息
- WakeLock被忽略 - 不再能唤醒CPU
- Alarm延迟执行 -
setExact()和setWindow()推迟到维护窗口 - WiFi扫描停止
- JobScheduler和SyncAdapter暂停
但系统会定期进入维护窗口(Maintenance Window),让应用执行延迟的任务:
makefile
初次进入Doze后的维护窗口时间间隔:
第1次: 1小时后,维护窗口5分钟
第2次: 2小时后,维护窗口5分钟
第3次: 4小时后,维护窗口5分钟
第4次及以后: 每6小时一次,维护窗口5分钟
如何适配Doze模式
错误做法 - 强行保持运行:
kotlin
// ❌ 不要申请白名单,这只是个临时方案
val intent = Intent().apply {
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
data = Uri.parse("package:$packageName")
}
startActivity(intent)
正确做法 - 使用JobScheduler:
kotlin
// ✅ 使用JobScheduler调度任务
val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
val job = JobInfo.Builder(JOB_ID, ComponentName(this, MyJobService::class.java))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true) // 设备重启后任务保留
.build()
jobScheduler.schedule(job)
kotlin
class MyJobService : JobService() {
override fun onStartJob(params: JobParameters?): Boolean {
// 执行后台任务
doWorkAsync {
// 任务完成后调用
jobFinished(params, false)
}
return true // 返回true表示任务还在执行
}
override fun onStopJob(params: JobParameters?): Boolean {
// 任务被系统中断时调用
return true // 返回true表示需要重新调度
}
}
使用WorkManager(推荐):
kotlin
// ✅ 更推荐使用WorkManager,它会自动处理兼容性
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
val syncWorkRequest = PeriodicWorkRequestBuilder<SyncWorker>(
15, TimeUnit.MINUTES
)
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueue(syncWorkRequest)
App Standby:待机桶管理
Android 9.0引入了App Standby Buckets(待机桶)机制,将应用分为5个优先级桶:
| 桶名称 | 说明 | 限制 |
|---|---|---|
| Active | 用户正在使用 | 无限制 |
| Working Set | 最近使用过 | 限制较少 |
| Frequent | 经常使用 | 中等限制 |
| Rare | 很少使用 | 严格限制 |
| Never | 从未使用或被禁用 | 极严格限制 |

系统会根据应用使用频率自动调整桶分类,分类越低,后台任务执行机会越少:
kotlin
// 查询当前应用所在的桶
val usageStatsManager = getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
val bucket = usageStatsManager.appStandbyBucket
val bucketName = when (bucket) {
UsageStatsManager.STANDBY_BUCKET_ACTIVE -> "Active"
UsageStatsManager.STANDBY_BUCKET_WORKING_SET -> "Working Set"
UsageStatsManager.STANDBY_BUCKET_FREQUENT -> "Frequent"
UsageStatsManager.STANDBY_BUCKET_RARE -> "Rare"
UsageStatsManager.STANDBY_BUCKET_NEVER -> "Never"
else -> "Unknown"
}
Log.d("Standby", "Current bucket: $bucketName")
Battery Historian:电量分析神器
Battery Historian是Google官方的电量分析工具,能够以可视化方式展示设备电量消耗的详细信息。
环境搭建
方法一:Docker方式(推荐)
bash
# 1. 安装Docker
# Linux: sudo apt install docker.io
# macOS: 下载Docker Desktop
# 2. 拉取Battery Historian镜像
docker pull gcr.io/android-battery-historian/stable:3.1
# 3. 运行容器
docker run -p 9999:9999 gcr.io/android-battery-historian/stable:3.1
# 4. 浏览器打开
# http://localhost:9999
方法二:源码编译(需要Go环境)
bash
# 1. 安装Go语言环境(1.11+)
# https://golang.org/dl/
# 2. 获取源码
go get -d -u github.com/google/battery-historian/...
# 3. 进入目录并运行
cd $GOPATH/src/github.com/google/battery-historian
go run cmd/battery-historian/battery-historian.go
# 4. 浏览器打开
# http://localhost:9999
数据采集流程
1. 重置电量统计
bash
# 重置电量统计数据
adb shell dumpsys batterystats --reset
2. 拔掉USB,进行测试操作
这一步非常重要:必须拔掉USB线,因为充电状态会影响很多行为。
执行你想分析的操作,比如:
- 启动应用并使用一段时间
- 让应用在后台运行
- 触发特定功能
3. 导出电量数据
bash
# 导出完整的电量统计数据
adb bugreport bugreport.zip
这个命令会生成一个zip文件,包含了详细的电量使用日志。
4. 上传到Battery Historian分析
打开 http://localhost:9999,上传 bugreport.zip 文件,就可以看到可视化的分析报告了。

报告解读:关键指标
Battery Historian的报告非常丰富,我们重点关注以下几个关键指标:
1. Battery Level(电量曲线)
最上方的绿色曲线显示电量变化。陡降说明耗电快,需要重点分析该时间段。
2. Screen(屏幕状态)
蓝色表示屏幕亮起,灰色表示熄屏。帮助区分前台和后台耗电。
3. Mobile Radio(移动网络状态)
显示网络状态:active > idle > off。频繁的active状态说明网络请求多。
4. GPS(定位状态)
绿色表示GPS开启。定位是耗电大户,要避免持续定位。
5. Wakelock(唤醒锁)
显示哪些应用持有Wakelock。这是重点,Wakelock滥用是耗电的主要原因之一。
6. App Usage(应用使用情况)
显示各应用的前台/后台运行时间和电量占比。
实战案例:定位耗电异常
场景: 用户反馈后台耗电严重。
分析步骤
- 查看电量曲线 - 发现夜间电量陡降15%
- 检查Screen状态 - 确认屏幕是熄灭的,排除用户操作
- 定位时间段 - 在凌晨2点到5点之间
- 检查Wakelock - 发现应用持有了大量Wakelock
- 查看具体Wakelock名称 -
SyncService$WakeLock持有了2小时45分钟!
问题定位: 同步服务的Wakelock没有正确释放,导致CPU无法睡眠。
修复方案:
kotlin
// ❌ 错误:Wakelock一直持有
val wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"SyncService::WakeLock"
)
wakeLock.acquire() // 获取后忘记释放
// ✅ 正确:设置超时时间
wakeLock.acquire(10 * 60 * 1000L) // 最多持有10分钟
// ✅ 更好:使用try-finally确保释放
wakeLock.acquire()
try {
doSync()
} finally {
if (wakeLock.isHeld) {
wakeLock.release()
}
}
Wakelock深度分析与优化
Wakelock类型
Android提供了几种Wakelock类型,从完全唤醒到仅保持CPU运行:
kotlin
// 1. PARTIAL_WAKE_LOCK - 只保持CPU运行,屏幕可以关闭(最常用)
val partialWakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"MyApp::PartialWakeLock"
)
// 2. SCREEN_DIM_WAKE_LOCK - 保持屏幕低亮度(已废弃)
// 3. SCREEN_BRIGHT_WAKE_LOCK - 保持屏幕高亮度(已废弃)
// 4. FULL_WAKE_LOCK - 完全唤醒(已废弃)
// 现代做法:使用WindowManager.LayoutParams
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
Wakelock最佳实践
1. 始终设置超时时间
kotlin
// ❌ 危险:没有超时时间
wakeLock.acquire()
// ✅ 安全:设置超时(毫秒)
wakeLock.acquire(10 * 60 * 1000L) // 10分钟后自动释放
2. 使用WakefulBroadcastReceiver(旧API)
kotlin
class MyWakefulReceiver : WakefulBroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 自动获取Wakelock
val componentName = startWakefulService(context, Intent(context, MyService::class.java))
}
}
class MyService : IntentService("MyService") {
override fun onHandleIntent(intent: Intent?) {
try {
// 处理任务
doWork()
} finally {
// 释放Wakelock
MyWakefulReceiver.completeWakefulIntent(intent)
}
}
}
3. 使用WorkManager(现代做法)
kotlin
// WorkManager自动管理Wakelock,无需手动处理
class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
// 任务执行期间,WorkManager自动持有Wakelock
doSyncWork()
return Result.success()
}
}
检测Wakelock滥用
使用adb命令
bash
# 查看当前所有Wakelock
adb shell dumpsys power | grep "Wake Locks"
# 查看详细的Wakelock统计
adb shell dumpsys batterystats | grep -A 20 "Wake lock"
使用Battery Historian
在Battery Historian报告中,点击 Wakelock 行,可以看到:
- 每个Wakelock的持有时长
- 触发次数
- 持有Wakelock的应用
如果发现某个Wakelock持有时间超过5分钟,基本可以判定为滥用。
Energy Profiler:实时电量监控
Android Studio 3.0+内置了Energy Profiler,可以实时监控应用的电量消耗。
使用步骤
-
打开Android Studio Profiler
- View → Tool Windows → Profiler
- 点击 "+" 选择设备和应用
-
选择Energy
- 点击 "ENERGY" 标签
-
查看能耗曲线
- 绿色:低能耗
- 黄色:中等能耗
- 红色:高能耗
-
分析能耗事件
- CPU
- Network
- Location
- Wake Lock

实战:定位网络耗电
Energy Profiler会标记出高能耗的网络请求。点击某个网络事件,可以看到:
- 请求URL
- 数据量
- 持续时间
- 调用堆栈
优化建议:
- 合并小请求,减少网络唤醒次数
- 使用批量接口
- 缓存可复用数据
- 在WiFi环境下预下载
网络性能监控
Network Profiler使用
Network Profiler是Android Studio内置的网络分析工具,可以实时查看:
- 网络请求时间线
- 上传/下载速度
- 请求详情(URL、Header、Body)
- 请求/响应耗时
使用步骤
- 打开Profiler - View → Tool Windows → Profiler
- 选择Network - 点击 "NETWORK" 标签
- 触发网络请求 - 使用应用,观察网络曲线
- 点击请求详情 - 查看完整的Request/Response信息

网络质量监控实现
手动实现一个简单的网络质量监控:
kotlin
class NetworkQualityMonitor(private val context: Context) {
private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
// 网络质量等级
enum class NetworkQuality {
EXCELLENT, // 优秀
GOOD, // 良好
MODERATE, // 中等
POOR, // 较差
VERY_POOR // 极差
}
// 获取当前网络类型
fun getNetworkType(): String {
val network = connectivityManager.activeNetwork ?: return "NONE"
val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return "NONE"
return when {
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> "WIFI"
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> {
// 获取具体的移动网络类型
val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
when (telephonyManager.dataNetworkType) {
TelephonyManager.NETWORK_TYPE_LTE -> "4G"
TelephonyManager.NETWORK_TYPE_NR -> "5G"
else -> "3G"
}
}
else -> "OTHER"
}
}
// 测量网络延迟(RTT)和带宽
suspend fun measureNetworkQuality(): NetworkQuality = withContext(Dispatchers.IO) {
val startTime = System.currentTimeMillis()
try {
// 1. 测试延迟 - ping一个小文件
val pingUrl = URL("https://www.google.com/generate_204")
val connection = pingUrl.openConnection() as HttpURLConnection
connection.requestMethod = "GET"
connection.connectTimeout = 5000
connection.readTimeout = 5000
val responseCode = connection.responseCode
val rtt = System.currentTimeMillis() - startTime
connection.disconnect()
// 2. 根据RTT判断网络质量
return@withContext when {
rtt < 100 -> NetworkQuality.EXCELLENT // <100ms 优秀
rtt < 300 -> NetworkQuality.GOOD // 100-300ms 良好
rtt < 600 -> NetworkQuality.MODERATE // 300-600ms 中等
rtt < 1000 -> NetworkQuality.POOR // 600-1000ms 较差
else -> NetworkQuality.VERY_POOR // >1000ms 极差
}
} catch (e: Exception) {
Log.e("NetworkQuality", "测量失败", e)
return@withContext NetworkQuality.VERY_POOR
}
}
// 获取网络信号强度(仅WiFi和移动网络)
@SuppressLint("MissingPermission")
fun getSignalStrength(): Int {
val network = connectivityManager.activeNetwork ?: return 0
val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return 0
return when {
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> {
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiInfo = wifiManager.connectionInfo
val level = WifiManager.calculateSignalLevel(wifiInfo.rssi, 5)
(level + 1) * 20 // 转换为0-100
}
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> {
// 需要 ACCESS_FINE_LOCATION 权限
val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
// 这里简化处理,实际需要使用 PhoneStateListener
50 // 默认返回50
}
else -> 0
}
}
}
使用示例:
kotlin
val monitor = NetworkQualityMonitor(context)
lifecycleScope.launch {
val networkType = monitor.getNetworkType()
val quality = monitor.measureNetworkQuality()
val signalStrength = monitor.getSignalStrength()
Log.d("Network", "类型: $networkType, 质量: $quality, 信号: $signalStrength%")
// 根据网络质量调整策略
when (quality) {
NetworkQuality.EXCELLENT, NetworkQuality.GOOD -> {
// 正常加载高清图片
loadHighQualityImages()
}
NetworkQuality.MODERATE -> {
// 加载中等质量图片
loadMediumQualityImages()
}
NetworkQuality.POOR, NetworkQuality.VERY_POOR -> {
// 加载低质量图片,减少请求
loadLowQualityImages()
showNetworkPoorTip()
}
}
}
弱网优化策略
弱网环境识别
除了上面的网络质量监控,我们还可以通过以下方式识别弱网:
kotlin
class WeakNetworkDetector {
// 方法1: 通过网络类型判断
fun isWeakNetwork(networkType: String): Boolean {
return networkType in listOf("2G", "3G", "EDGE", "GPRS")
}
// 方法2: 通过请求超时次数判断
private var consecutiveTimeouts = 0
fun onRequestTimeout() {
consecutiveTimeouts++
if (consecutiveTimeouts >= 3) {
// 连续3次超时,判定为弱网
enableWeakNetworkMode()
}
}
fun onRequestSuccess() {
consecutiveTimeouts = 0
}
// 方法3: 通过下载速度判断
fun isSlowDownload(bytesDownloaded: Long, durationMs: Long): Boolean {
val speedKBps = bytesDownloaded / 1024.0 / (durationMs / 1000.0)
return speedKBps < 50 // 低于50KB/s认为是弱网
}
}
超时策略设置
动态超时 - 根据网络质量调整:
kotlin
object TimeoutConfig {
fun getConnectTimeout(networkQuality: NetworkQuality): Long {
return when (networkQuality) {
NetworkQuality.EXCELLENT -> 5_000L // 5秒
NetworkQuality.GOOD -> 8_000L // 8秒
NetworkQuality.MODERATE -> 15_000L // 15秒
NetworkQuality.POOR -> 25_000L // 25秒
NetworkQuality.VERY_POOR -> 40_000L // 40秒
}
}
fun getReadTimeout(networkQuality: NetworkQuality): Long {
return getConnectTimeout(networkQuality) * 2 // 读取超时是连接超时的2倍
}
}
// 使用OkHttp配置动态超时
val client = OkHttpClient.Builder()
.connectTimeout(TimeoutConfig.getConnectTimeout(currentQuality), TimeUnit.MILLISECONDS)
.readTimeout(TimeoutConfig.getReadTimeout(currentQuality), TimeUnit.MILLISECONDS)
.build()
重试机制优化
指数退避重试:
kotlin
class RetryInterceptor(private val maxRetries: Int = 3) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
var response: Response? = null
var retryCount = 0
var exception: IOException? = null
while (retryCount < maxRetries) {
try {
response = chain.proceed(request)
// 成功,返回结果
if (response.isSuccessful) {
return response
}
// 服务器错误(5xx)才重试,客户端错误(4xx)不重试
if (response.code !in 500..599) {
return response
}
} catch (e: IOException) {
exception = e
Log.w("RetryInterceptor", "请求失败,准备重试: ${retryCount + 1}/$maxRetries", e)
}
retryCount++
// 指数退避: 1秒 → 2秒 → 4秒
val delayMs = (1000 * Math.pow(2.0, retryCount.toDouble() - 1)).toLong()
Thread.sleep(delayMs)
}
// 所有重试都失败,抛出异常或返回最后的响应
return response ?: throw exception ?: IOException("请求失败且无响应")
}
}
// 使用
val client = OkHttpClient.Builder()
.addInterceptor(RetryInterceptor(maxRetries = 3))
.build()
降级策略
当网络极差时,启用降级策略:
kotlin
class NetworkDegradationStrategy(private val networkMonitor: NetworkQualityMonitor) {
suspend fun <T> executeWithDegradation(
normalAction: suspend () -> T,
degradedAction: suspend () -> T
): T {
val quality = networkMonitor.measureNetworkQuality()
return if (quality in listOf(NetworkQuality.POOR, NetworkQuality.VERY_POOR)) {
Log.d("Degradation", "网络较差,使用降级策略")
degradedAction()
} else {
normalAction()
}
}
}
// 使用示例
val strategy = NetworkDegradationStrategy(networkMonitor)
val articles = strategy.executeWithDegradation(
normalAction = {
// 正常情况:加载完整文章列表(包含图片)
api.getArticleListWithImages()
},
degradedAction = {
// 降级:只加载标题列表(不含图片)
api.getArticleListTitleOnly()
}
)
数据压缩
使用Gzip压缩请求和响应:
kotlin
// OkHttp默认支持Gzip,但要确保服务器也支持
val client = OkHttpClient.Builder()
.addInterceptor { chain ->
val original = chain.request()
// 请求时添加Accept-Encoding
val request = original.newBuilder()
.header("Accept-Encoding", "gzip")
.build()
chain.proceed(request)
}
.build()
对于上传,也可以手动Gzip压缩:
kotlin
fun compressRequestBody(body: RequestBody): RequestBody {
return object : RequestBody() {
override fun contentType() = body.contentType()
override fun contentLength() = -1L // 不知道压缩后的大小
override fun writeTo(sink: BufferedSink) {
val gzipSink = GzipSink(sink)
val gzipBuffer = gzipSink.buffer()
body.writeTo(gzipBuffer)
gzipBuffer.close()
}
}
}
请求合并
将多个小请求合并为一个批量请求:
kotlin
// ❌ 不好:多次请求
suspend fun loadUserProfiles(userIds: List<String>) {
userIds.forEach { userId ->
val profile = api.getUserProfile(userId) // N次网络请求
updateUI(profile)
}
}
// ✅ 更好:批量请求
suspend fun loadUserProfiles(userIds: List<String>) {
val profiles = api.getBatchUserProfiles(userIds) // 1次网络请求
profiles.forEach { profile ->
updateUI(profile)
}
}
kotlin
// API定义
interface UserApi {
@GET("user/{userId}")
suspend fun getUserProfile(@Path("userId") userId: String): UserProfile
@POST("users/batch")
suspend fun getBatchUserProfiles(@Body userIds: List<String>): List<UserProfile>
}
离线缓存策略
使用OkHttp的缓存机制:
kotlin
// 1. 配置缓存目录和大小
val cacheSize = 50L * 1024 * 1024 // 50MB
val cache = Cache(context.cacheDir, cacheSize)
val client = OkHttpClient.Builder()
.cache(cache)
.addNetworkInterceptor(CacheInterceptor())
.build()
class CacheInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
// 弱网时优先使用缓存
if (isWeakNetwork()) {
request = request.newBuilder()
.cacheControl(
CacheControl.Builder()
.maxStale(7, TimeUnit.DAYS) // 缓存有效期7天
.build()
)
.build()
}
val response = chain.proceed(request)
// 对响应设置缓存策略
return response.newBuilder()
.header("Cache-Control", "public, max-age=3600") // 缓存1小时
.removeHeader("Pragma")
.build()
}
}
更完整的缓存策略:
kotlin
class SmartCacheStrategy(
private val context: Context,
private val networkMonitor: NetworkQualityMonitor
) {
private val sharedPrefs = context.getSharedPreferences("cache_meta", Context.MODE_PRIVATE)
// 判断缓存是否可用
fun isCacheValid(key: String, maxAgeSeconds: Long): Boolean {
val cachedTime = sharedPrefs.getLong(key, 0)
val age = (System.currentTimeMillis() - cachedTime) / 1000
return age < maxAgeSeconds
}
// 保存缓存时间戳
fun markCached(key: String) {
sharedPrefs.edit().putLong(key, System.currentTimeMillis()).apply()
}
// 根据网络质量决定缓存策略
suspend fun getCacheMaxAge(networkQuality: NetworkQuality): Long {
return when (networkQuality) {
NetworkQuality.EXCELLENT, NetworkQuality.GOOD -> 300L // 5分钟
NetworkQuality.MODERATE -> 1800L // 30分钟
NetworkQuality.POOR, NetworkQuality.VERY_POOR -> 86400L // 1天
}
}
}
网络优化最佳实践
1. DNS优化
使用HTTPDNS避免DNS劫持和提升解析速度:
kotlin
// 使用阿里云HTTPDNS(示例)
class HttpDnsInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()
val url = original.url
// 使用HTTPDNS解析IP
val ip = HttpDns.getIpByHost(url.host)
if (ip != null) {
val newUrl = url.newBuilder()
.host(ip)
.build()
val newRequest = original.newBuilder()
.url(newUrl)
.header("Host", url.host) // 保留原始Host
.build()
return chain.proceed(newRequest)
}
return chain.proceed(original)
}
}
2. 连接复用 - HTTP/2
使用OkHttp默认支持HTTP/2,实现请求多路复用:
kotlin
val client = OkHttpClient.Builder()
.protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1)) // 支持HTTP/2
.build()
HTTP/2的优势:
- 多路复用 - 一个连接可以并发多个请求
- 头部压缩 - 减少重复头部的传输
- 服务器推送 - 主动推送资源
3. 图片加载优化
使用Glide/Coil进行智能加载:
kotlin
// 根据网络质量加载不同分辨率的图片
fun loadImage(imageView: ImageView, imageUrl: String, networkQuality: NetworkQuality) {
val url = when (networkQuality) {
NetworkQuality.EXCELLENT, NetworkQuality.GOOD -> "${imageUrl}?quality=high"
NetworkQuality.MODERATE -> "${imageUrl}?quality=medium"
NetworkQuality.POOR, NetworkQuality.VERY_POOR -> "${imageUrl}?quality=low"
}
Glide.with(imageView.context)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(imageView)
}
4. 预加载策略
在WiFi环境下预加载可能需要的资源:
kotlin
class PreloadManager(private val context: Context) {
fun preloadIfNeeded() {
if (isWiFiConnected() && !isBatterySaveMode()) {
lifecycleScope.launch {
// 预加载今日推荐文章
preloadArticles()
// 预加载视频封面
preloadVideoThumbnails()
}
}
}
private fun isWiFiConnected(): Boolean {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = cm.activeNetwork ?: return false
val capabilities = cm.getNetworkCapabilities(network) ?: return false
return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
}
private fun isBatterySaveMode(): Boolean {
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
return pm.isPowerSaveMode
}
}
5. 流量控制
限制后台流量使用:
kotlin
class TrafficController {
private val maxDailyTraffic = 50 * 1024 * 1024L // 50MB
private val prefs = context.getSharedPreferences("traffic", Context.MODE_PRIVATE)
fun canUseNetwork(): Boolean {
val today = getToday()
val usedTraffic = prefs.getLong("traffic_$today", 0)
return usedTraffic < maxDailyTraffic
}
fun recordTraffic(bytes: Long) {
val today = getToday()
val used = prefs.getLong("traffic_$today", 0)
prefs.edit().putLong("traffic_$today", used + bytes).apply()
}
private fun getToday(): String {
return SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(Date())
}
}

实战案例
案例1: 后台定位任务电量优化
问题: 地图应用后台持续定位导致电量消耗严重。
优化前:
kotlin
// ❌ 持续高精度定位
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
1000L, // 1秒更新一次
0f,
locationListener
)
电量消耗: 30%/小时
优化后:
kotlin
// ✅ 动态调整定位策略
class SmartLocationManager(private val context: Context) {
private val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
private val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
fun startSmartLocationUpdates(listener: LocationListener) {
val (interval, provider) = getOptimalLocationStrategy()
locationManager.requestLocationUpdates(
provider,
interval,
0f,
listener
)
}
private fun getOptimalLocationStrategy(): Pair<Long, String> {
return when {
// 省电模式:使用网络定位,30秒更新
powerManager.isPowerSaveMode -> {
30_000L to LocationManager.NETWORK_PROVIDER
}
// 前台运行:使用GPS,5秒更新
isAppInForeground() -> {
5_000L to LocationManager.GPS_PROVIDER
}
// 后台运行:使用网络定位,5分钟更新
else -> {
300_000L to LocationManager.NETWORK_PROVIDER
}
}
}
}
优化结果:
- 电量消耗降至 8%/小时
- 减少了 73% 的电量消耗
- 用户续航时间提升 3倍
案例2: 弱网环境下载优化
问题: 在地铁等弱网环境下,文件下载经常失败。
优化前:
kotlin
// ❌ 单次下载,失败后从头开始
suspend fun downloadFile(url: String, outputFile: File) {
URL(url).openStream().use { input ->
FileOutputStream(outputFile).use { output ->
input.copyTo(output)
}
}
}
成功率 : 弱网下只有 40%
优化后 - 实现断点续传:
kotlin
// ✅ 支持断点续传的下载器
class ResumableDownloader {
suspend fun downloadWithResume(
url: String,
outputFile: File,
maxRetries: Int = 5
): Result<File> = withContext(Dispatchers.IO) {
var retryCount = 0
var lastException: Exception? = null
while (retryCount < maxRetries) {
try {
val downloaded = if (outputFile.exists()) outputFile.length() else 0
val connection = URL(url).openConnection() as HttpURLConnection
connection.requestMethod = "GET"
// 设置断点续传
if (downloaded > 0) {
connection.setRequestProperty("Range", "bytes=$downloaded-")
Log.d("Download", "从 $downloaded 字节继续下载")
}
// 检查是否支持断点续传
val responseCode = connection.responseCode
if (responseCode != HttpURLConnection.HTTP_OK &&
responseCode != HttpURLConnection.HTTP_PARTIAL) {
return@withContext Result.failure(IOException("服务器不支持断点续传"))
}
// 下载数据
connection.inputStream.use { input ->
FileOutputStream(outputFile, true).use { output -> // append=true
val buffer = ByteArray(8192)
var bytesRead: Int
var totalBytes = downloaded
while (input.read(buffer).also { bytesRead = it } != -1) {
output.write(buffer, 0, bytesRead)
totalBytes += bytesRead
// 更新进度
val progress = (totalBytes * 100 / connection.contentLength).toInt()
Log.d("Download", "下载进度: $progress%")
}
}
}
connection.disconnect()
return@withContext Result.success(outputFile)
} catch (e: Exception) {
lastException = e
retryCount++
Log.w("Download", "下载失败,重试 $retryCount/$maxRetries", e)
// 指数退避
delay(1000 * (1 shl retryCount))
}
}
Result.failure(lastException ?: IOException("下载失败"))
}
}
优化结果:
- 成功率提升至 95%
- 平均下载时间减少 60%(减少重复下载)
- 用户体验显著改善

总结
电量优化和网络优化是Android应用性能优化中至关重要的两个方向,它们直接影响用户体验和应用口碑。
核心要点回顾
电量优化:
- 理解Doze模式和App Standby机制,使用JobScheduler/WorkManager适配
- 使用Battery Historian分析电量消耗,定位异常
- 避免Wakelock滥用,始终设置超时时间
- 优化定位策略,根据场景动态调整
- 减少后台网络请求,合并任务
网络优化:
- 识别弱网环境,动态调整超时和重试策略
- 实现智能降级,保证弱网下的基本可用性
- 使用数据压缩、请求合并减少流量
- 启用HTTP/2连接复用
- 实现离线缓存和断点续传
优化检查清单
- 后台任务使用WorkManager调度
- 为Wakelock设置超时时间
- 定位服务根据前后台动态调整
- 网络请求设置合理超时
- 实现指数退避重试机制
- 弱网下启用降级策略
- 启用请求响应压缩
- 使用OkHttp缓存机制
- WiFi环境下预加载资源
- 使用Battery Historian定期分析
参考资料
官方资料:
系列文章:
作者简介: 多年Android系统开发经验,专注于系统稳定性与性能优化领域。欢迎关注本系列,一起深入Android系统的精彩世界!