15 KiB
15 KiB
电量优化
目录
电量消耗分析
电量消耗主要来源
- CPU:频繁计算、长时间运行
- 网络:频繁请求、大数据传输
- 定位:GPS、网络定位
- 屏幕:高亮度、长时间亮屏
- 传感器:加速度计、陀螺仪等
- WakeLock:阻止系统休眠
电量消耗分析工具
1. Battery Historian
# 收集电池数据
adb shell dumpsys batterystats --reset
# 使用应用一段时间
adb shell dumpsys batterystats > batterystats.txt
# 生成报告
python historian.py batterystats.txt > battery.html
2. Battery Profiler
- Android Studio -> View -> Tool Windows -> Profiler
- 实时查看电量消耗
- 分析电量消耗趋势
- 查看唤醒锁使用情况
3. adb 命令
# 查看电量信息
adb shell dumpsys battery
# 查看电量统计
adb shell dumpsys batterystats
# 查看唤醒锁
adb shell dumpsys power
WakeLock使用
WakeLock 简介
WakeLock 用于阻止系统进入休眠状态,保持 CPU 运行或屏幕亮起。
WakeLock 类型
// PARTIAL_WAKE_LOCK:保持 CPU 运行,屏幕可以关闭
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock"
);
// SCREEN_DIM_WAKE_LOCK:保持屏幕变暗,CPU 运行(已废弃)
// SCREEN_BRIGHT_WAKE_LOCK:保持屏幕高亮,CPU 运行(已废弃)
// FULL_WAKE_LOCK:保持屏幕高亮,键盘背光,CPU 运行(已废弃)
// ACQUIRE_CAUSES_WAKEUP:获取时立即唤醒屏幕
// ON_AFTER_RELEASE:释放后保持屏幕亮起一段时间
WakeLock 使用示例
public class WakeLockManager {
private PowerManager powerManager;
private PowerManager.WakeLock wakeLock;
public WakeLockManager(Context context) {
powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"MyApp::WakeLock"
);
}
public void acquire() {
if (!wakeLock.isHeld()) {
wakeLock.acquire();
}
}
public void release() {
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
public void acquire(long timeout) {
if (!wakeLock.isHeld()) {
wakeLock.acquire(timeout); // 超时自动释放
}
}
}
// 使用
WakeLockManager manager = new WakeLockManager(context);
manager.acquire();
// 执行需要保持 CPU 运行的任务
manager.release();
WakeLock 注意事项
// ❌ 错误示例:忘记释放 WakeLock
public void doWork() {
wakeLock.acquire();
// 如果这里抛出异常,WakeLock 不会被释放
doSomething();
wakeLock.release();
}
// ✅ 正确做法:使用 try-finally
public void doWork() {
wakeLock.acquire();
try {
doSomething();
} finally {
wakeLock.release();
}
}
// ✅ 使用超时自动释放
wakeLock.acquire(10 * 60 * 1000L); // 10分钟后自动释放
WakeLock 最佳实践
- 及时释放:使用完毕后立即释放
- 使用超时:设置超时时间,避免忘记释放
- 最小化使用:只在必要时使用,尽量减少使用时间
- 使用替代方案:优先使用 JobScheduler、WorkManager
JobScheduler
JobScheduler 简介
JobScheduler 是 Android 5.0+ 提供的任务调度框架,可以在合适的时机执行任务,节省电量。
JobScheduler 优势
- 智能调度:系统选择最佳时机执行
- 批量执行:合并多个任务一起执行
- 条件触发:满足条件时才执行
- 电量友好:减少电量消耗
JobScheduler 使用示例
public class MyJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
// 执行任务(在后台线程)
new Thread(() -> {
// 执行任务
doWork();
// 任务完成
jobFinished(params, false);
}).start();
return true; // 返回 true 表示任务在后台执行
}
@Override
public boolean onStopJob(JobParameters params) {
// 任务被取消
return false; // 返回 false 表示任务不需要重新调度
}
private void doWork() {
// 执行具体任务
}
}
public class JobSchedulerHelper {
private JobScheduler jobScheduler;
public JobSchedulerHelper(Context context) {
jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
}
public void scheduleJob() {
JobInfo jobInfo = new JobInfo.Builder(1, new ComponentName(context, MyJobService.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) // 需要网络
.setRequiresCharging(false) // 不需要充电
.setRequiresDeviceIdle(false) // 不需要设备空闲
.setPeriodic(15 * 60 * 1000) // 每15分钟执行一次
.setPersisted(true) // 持久化,重启后仍然有效
.build();
jobScheduler.schedule(jobInfo);
}
public void cancelJob(int jobId) {
jobScheduler.cancel(jobId);
}
public void cancelAllJobs() {
jobScheduler.cancelAll();
}
}
JobScheduler 条件
JobInfo jobInfo = new JobInfo.Builder(1, componentName)
// 网络条件
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE) // 不需要网络
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) // 任何网络
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) // 非计费网络(WiFi)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING) // 非漫游网络
// 充电条件
.setRequiresCharging(true) // 需要充电
// 设备空闲条件
.setRequiresDeviceIdle(true) // 需要设备空闲
// 存储条件
.setRequiresStorageNotLow(true) // 存储空间充足
// 电池条件
.setRequiresBatteryNotLow(true) // 电池电量充足
// 执行时间
.setMinimumLatency(1000) // 最小延迟1秒
.setOverrideDeadline(5000) // 最晚5秒后执行
.setPeriodic(15 * 60 * 1000) // 每15分钟执行一次
.build();
WorkManager
WorkManager 简介
WorkManager 是 Android Jetpack 提供的任务调度框架,兼容 Android 4.0+,是 JobScheduler、Firebase JobDispatcher、AlarmManager 的统一封装。
WorkManager 优势
- 向后兼容:兼容所有 Android 版本
- 智能调度:自动选择最佳调度方式
- 链式任务:支持任务链和任务组
- 约束条件:支持多种约束条件
- 持久化:任务持久化,应用重启后仍然有效
WorkManager 使用示例
1. 定义 Worker
public class MyWorker extends Worker {
public MyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
// 执行任务
try {
doSomething();
return Result.success();
} catch (Exception e) {
return Result.retry(); // 重试
// 或
// return Result.failure(); // 失败
}
}
private void doSomething() {
// 执行具体任务
}
}
2. 创建 WorkRequest
// 一次性任务
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWorker.class)
.setConstraints(constraints)
.setInitialDelay(10, TimeUnit.SECONDS)
.addTag("my_tag")
.build();
// 周期性任务
PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(
MyWorker.class,
15, TimeUnit.MINUTES
).build();
3. 设置约束条件
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 需要网络连接
.setRequiresCharging(true) // 需要充电
.setRequiresDeviceIdle(true) // 需要设备空闲
.setRequiresBatteryNotLow(true) // 电池电量充足
.setRequiresStorageNotLow(true) // 存储空间充足
.build();
4. 提交任务
WorkManager workManager = WorkManager.getInstance(context);
// 提交一次性任务
workManager.enqueue(workRequest);
// 提交周期性任务
workManager.enqueue(periodicWorkRequest);
// 取消任务
workManager.cancelWorkById(workRequest.getId());
workManager.cancelAllWorkByTag("my_tag");
workManager.cancelAllWork();
5. 观察任务状态
workManager.getWorkInfoByIdLiveData(workRequest.getId())
.observe(lifecycleOwner, workInfo -> {
if (workInfo != null) {
WorkInfo.State state = workInfo.getState();
if (state == WorkInfo.State.SUCCEEDED) {
// 任务成功
} else if (state == WorkInfo.State.FAILED) {
// 任务失败
}
}
});
6. 链式任务
// 顺序执行
WorkManager workManager = WorkManager.getInstance(context);
workManager
.beginWith(workRequest1)
.then(workRequest2)
.then(workRequest3)
.enqueue();
// 并行执行
workManager
.beginWith(workRequest1, workRequest2)
.then(workRequest3)
.enqueue();
后台任务优化
1. 减少后台任务频率
// ❌ 错误示例:频繁执行后台任务
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
syncData(); // 每分钟同步一次
}
}, 0, 60 * 1000);
// ✅ 正确做法:使用 WorkManager 智能调度
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(
SyncWorker.class,
15, TimeUnit.MINUTES
).build();
WorkManager.getInstance(context).enqueue(workRequest);
2. 批量处理任务
// ❌ 错误示例:逐个处理
for (Item item : items) {
processItem(item); // 每个任务都唤醒设备
}
// ✅ 正确做法:批量处理
List<Item> batch = new ArrayList<>();
for (Item item : items) {
batch.add(item);
if (batch.size() >= 10) {
processBatch(batch); // 批量处理
batch.clear();
}
}
3. 使用前台服务
// 对于需要长时间运行的任务,使用前台服务
public class MyForegroundService extends Service {
@Override
public void onCreate() {
super.onCreate();
Notification notification = createNotification();
startForeground(1, notification);
}
private Notification createNotification() {
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("正在运行")
.setContentText("任务进行中")
.setSmallIcon(R.drawable.ic_notification)
.build();
}
}
4. 避免长时间运行
// ❌ 错误示例:长时间运行
public void doWork() {
while (true) {
processData();
Thread.sleep(1000);
}
}
// ✅ 正确做法:使用 WorkManager 或 JobScheduler
// 让系统决定何时执行任务
5. 优化网络请求
// ❌ 错误示例:频繁请求
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
api.getData().enqueue(callback); // 每分钟请求一次
}
}, 0, 60 * 1000);
// ✅ 正确做法:使用推送或智能调度
// 使用 FCM 推送通知,或使用 WorkManager 在合适时机请求
电量优化工具
1. Battery Historian
# 收集数据
adb shell dumpsys batterystats --reset
# 使用应用
adb shell dumpsys batterystats > batterystats.txt
# 生成报告
python historian.py batterystats.txt > battery.html
2. Battery Profiler
- Android Studio -> View -> Tool Windows -> Profiler
- 实时查看电量消耗
- 分析唤醒锁使用
- 查看 CPU 使用情况
3. adb 命令
# 查看电量信息
adb shell dumpsys battery
# 查看唤醒锁
adb shell dumpsys power
# 查看电量统计
adb shell dumpsys batterystats
4. Doze 模式测试
# 进入 Doze 模式
adb shell dumpsys deviceidle force-idle
# 退出 Doze 模式
adb shell dumpsys deviceidle unforce
# 查看 Doze 状态
adb shell dumpsys deviceidle
面试常见问题
Q1: 如何优化应用电量消耗?
答案:
- 减少后台任务:使用 JobScheduler、WorkManager 智能调度
- 优化网络请求:减少请求频率、批量处理
- 合理使用 WakeLock:及时释放、使用超时
- 优化定位:使用缓存、降低定位频率
- 优化屏幕:降低亮度、及时关闭屏幕
- 优化 CPU:减少计算、使用异步处理
Q2: WakeLock 的作用和使用注意事项?
答案:
- 作用:阻止系统进入休眠状态
- 类型:PARTIAL_WAKE_LOCK(保持 CPU)、SCREEN_BRIGHT_WAKE_LOCK(保持屏幕)
- 注意事项:
- 及时释放,避免忘记释放
- 使用超时,设置自动释放时间
- 最小化使用,只在必要时使用
- 优先使用 JobScheduler、WorkManager
Q3: JobScheduler 和 WorkManager 的区别?
答案:
-
JobScheduler:
- Android 5.0+ 支持
- 系统级 API
- 功能相对简单
-
WorkManager:
- Android 4.0+ 支持(向后兼容)
- Jetpack 组件
- 功能更强大,支持链式任务、任务组
- 自动选择最佳调度方式
Q4: 如何分析应用电量消耗?
答案:
- Battery Historian:生成详细的电量消耗报告
- Battery Profiler:实时查看电量消耗
- adb 命令:查看电量统计、唤醒锁使用
- 系统设置:查看应用电量使用情况
Q5: Doze 模式对应用的影响?
答案:
- Doze 模式:Android 6.0+ 的省电模式
- 影响:
- 限制网络访问
- 延迟后台任务
- 限制唤醒锁
- 应对:
- 使用 WorkManager 或 JobScheduler
- 使用前台服务(重要任务)
- 使用 FCM 高优先级消息
Q6: 后台任务优化的最佳实践?
答案:
- 使用 WorkManager 或 JobScheduler 智能调度
- 减少任务执行频率
- 批量处理任务
- 使用约束条件(网络、充电等)
- 避免长时间运行的任务
- 使用前台服务(重要任务)
总结
电量优化是 Android 性能优化的重要部分,主要从以下几个方面入手:
- 减少后台任务:使用智能调度框架
- 优化网络请求:减少频率、批量处理
- 合理使用 WakeLock:及时释放、最小化使用
- 使用约束条件:只在满足条件时执行任务
- 监控电量消耗:使用工具分析电量使用情况
通过合理的电量优化,可以延长设备电池续航时间,提升用户体验。
最后更新:2024年