# 电量优化

## 目录
- [电量消耗分析](#电量消耗分析)
- [WakeLock使用](#wakelock使用)
- [JobScheduler](#jobscheduler)
- [WorkManager](#workmanager)
- [后台任务优化](#后台任务优化)
- [电量优化工具](#电量优化工具)
- [面试常见问题](#面试常见问题)

---

## 电量消耗分析

### 电量消耗主要来源

1. **CPU**：频繁计算、长时间运行
2. **网络**：频繁请求、大数据传输
3. **定位**：GPS、网络定位
4. **屏幕**：高亮度、长时间亮屏
5. **传感器**：加速度计、陀螺仪等
6. **WakeLock**：阻止系统休眠

### 电量消耗分析工具

#### 1. Battery Historian

```bash
# 收集电池数据
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 命令

```bash
# 查看电量信息
adb shell dumpsys battery

# 查看电量统计
adb shell dumpsys batterystats

# 查看唤醒锁
adb shell dumpsys power
```

---

## WakeLock使用

### WakeLock 简介

WakeLock 用于阻止系统进入休眠状态，保持 CPU 运行或屏幕亮起。

### WakeLock 类型

```java
// 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 使用示例

```java
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 注意事项

```java
// ❌ 错误示例：忘记释放 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 最佳实践

1. **及时释放**：使用完毕后立即释放
2. **使用超时**：设置超时时间，避免忘记释放
3. **最小化使用**：只在必要时使用，尽量减少使用时间
4. **使用替代方案**：优先使用 JobScheduler、WorkManager

---

## JobScheduler

### JobScheduler 简介

JobScheduler 是 Android 5.0+ 提供的任务调度框架，可以在合适的时机执行任务，节省电量。

### JobScheduler 优势

1. **智能调度**：系统选择最佳时机执行
2. **批量执行**：合并多个任务一起执行
3. **条件触发**：满足条件时才执行
4. **电量友好**：减少电量消耗

### JobScheduler 使用示例

```java
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() {
        // 执行具体任务
    }
}
```

```java
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 条件

```java
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 优势

1. **向后兼容**：兼容所有 Android 版本
2. **智能调度**：自动选择最佳调度方式
3. **链式任务**：支持任务链和任务组
4. **约束条件**：支持多种约束条件
5. **持久化**：任务持久化，应用重启后仍然有效

### WorkManager 使用示例

#### 1. 定义 Worker

```java
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

```java
// 一次性任务
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. 设置约束条件

```java
Constraints constraints = new Constraints.Builder()
    .setRequiredNetworkType(NetworkType.CONNECTED) // 需要网络连接
    .setRequiresCharging(true) // 需要充电
    .setRequiresDeviceIdle(true) // 需要设备空闲
    .setRequiresBatteryNotLow(true) // 电池电量充足
    .setRequiresStorageNotLow(true) // 存储空间充足
    .build();
```

#### 4. 提交任务

```java
WorkManager workManager = WorkManager.getInstance(context);

// 提交一次性任务
workManager.enqueue(workRequest);

// 提交周期性任务
workManager.enqueue(periodicWorkRequest);

// 取消任务
workManager.cancelWorkById(workRequest.getId());
workManager.cancelAllWorkByTag("my_tag");
workManager.cancelAllWork();
```

#### 5. 观察任务状态

```java
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. 链式任务

```java
// 顺序执行
WorkManager workManager = WorkManager.getInstance(context);
workManager
    .beginWith(workRequest1)
    .then(workRequest2)
    .then(workRequest3)
    .enqueue();

// 并行执行
workManager
    .beginWith(workRequest1, workRequest2)
    .then(workRequest3)
    .enqueue();
```

---

## 后台任务优化

### 1. 减少后台任务频率

```java
// ❌ 错误示例：频繁执行后台任务
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. 批量处理任务

```java
// ❌ 错误示例：逐个处理
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. 使用前台服务

```java
// 对于需要长时间运行的任务，使用前台服务
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. 避免长时间运行

```java
// ❌ 错误示例：长时间运行
public void doWork() {
    while (true) {
        processData();
        Thread.sleep(1000);
    }
}

// ✅ 正确做法：使用 WorkManager 或 JobScheduler
// 让系统决定何时执行任务
```

### 5. 优化网络请求

```java
// ❌ 错误示例：频繁请求
Timer timer = new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        api.getData().enqueue(callback); // 每分钟请求一次
    }
}, 0, 60 * 1000);

// ✅ 正确做法：使用推送或智能调度
// 使用 FCM 推送通知，或使用 WorkManager 在合适时机请求
```

---

## 电量优化工具

### 1. Battery Historian

```bash
# 收集数据
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 命令

```bash
# 查看电量信息
adb shell dumpsys battery

# 查看唤醒锁
adb shell dumpsys power

# 查看电量统计
adb shell dumpsys batterystats
```

### 4. Doze 模式测试

```bash
# 进入 Doze 模式
adb shell dumpsys deviceidle force-idle

# 退出 Doze 模式
adb shell dumpsys deviceidle unforce

# 查看 Doze 状态
adb shell dumpsys deviceidle
```

---

## 面试常见问题

### Q1: 如何优化应用电量消耗？

**答案：**
1. **减少后台任务**：使用 JobScheduler、WorkManager 智能调度
2. **优化网络请求**：减少请求频率、批量处理
3. **合理使用 WakeLock**：及时释放、使用超时
4. **优化定位**：使用缓存、降低定位频率
5. **优化屏幕**：降低亮度、及时关闭屏幕
6. **优化 CPU**：减少计算、使用异步处理

### Q2: WakeLock 的作用和使用注意事项？

**答案：**
- **作用**：阻止系统进入休眠状态
- **类型**：PARTIAL_WAKE_LOCK（保持 CPU）、SCREEN_BRIGHT_WAKE_LOCK（保持屏幕）
- **注意事项**：
  1. 及时释放，避免忘记释放
  2. 使用超时，设置自动释放时间
  3. 最小化使用，只在必要时使用
  4. 优先使用 JobScheduler、WorkManager

### Q3: JobScheduler 和 WorkManager 的区别？

**答案：**
- **JobScheduler**：
  - Android 5.0+ 支持
  - 系统级 API
  - 功能相对简单
  
- **WorkManager**：
  - Android 4.0+ 支持（向后兼容）
  - Jetpack 组件
  - 功能更强大，支持链式任务、任务组
  - 自动选择最佳调度方式

### Q4: 如何分析应用电量消耗？

**答案：**
1. **Battery Historian**：生成详细的电量消耗报告
2. **Battery Profiler**：实时查看电量消耗
3. **adb 命令**：查看电量统计、唤醒锁使用
4. **系统设置**：查看应用电量使用情况

### Q5: Doze 模式对应用的影响？

**答案：**
- **Doze 模式**：Android 6.0+ 的省电模式
- **影响**：
  1. 限制网络访问
  2. 延迟后台任务
  3. 限制唤醒锁
- **应对**：
  1. 使用 WorkManager 或 JobScheduler
  2. 使用前台服务（重要任务）
  3. 使用 FCM 高优先级消息

### Q6: 后台任务优化的最佳实践？

**答案：**
1. 使用 WorkManager 或 JobScheduler 智能调度
2. 减少任务执行频率
3. 批量处理任务
4. 使用约束条件（网络、充电等）
5. 避免长时间运行的任务
6. 使用前台服务（重要任务）

---

## 总结

电量优化是 Android 性能优化的重要部分，主要从以下几个方面入手：

1. **减少后台任务**：使用智能调度框架
2. **优化网络请求**：减少频率、批量处理
3. **合理使用 WakeLock**：及时释放、最小化使用
4. **使用约束条件**：只在满足条件时执行任务
5. **监控电量消耗**：使用工具分析电量使用情况

通过合理的电量优化，可以延长设备电池续航时间，提升用户体验。

---

*最后更新：2024年*
