PowerManager(电源服务)
使用 PowerManager (电源服务)
PowerManager
是 Android 提供的一个系统服务类,用于管理和查询设备的电源状态,如屏幕唤醒、锁屏、电池优化等。它允许开发者控制设备的电源行为(如保持屏幕常亮)或检测电源状态(如是否充电)。结合 Android 动画合集的背景,PowerManager
可用于在动画运行时保持屏幕开启,或根据电源状态调整动画行为(如低电量时降低动画频率)。本文将详细介绍 PowerManager
的功能、使用方法、权限要求及注意事项,重点展示如何使用 WakeLock
和电源状态检测,并结合动画实现交互效果。
PowerManager 的作用与原理
- 作用:
PowerManager
提供对设备电源管理的访问,包括屏幕唤醒锁(WakeLock)、CPU 控制、电源模式(如低电量模式)以及电池状态查询。它适合需要控制设备休眠或响应电源变化的场景。 - 原理:
PowerManager
通过 Android 的电源管理框架与底层硬件交互,管理屏幕、CPU 和系统的休眠状态。WakeLock
是其核心功能,用于防止设备进入休眠状态,确保任务(如动画、后台服务)持续运行。 - 应用场景:
- 保持屏幕常亮(如播放动画或视频时)。
- 检测电池状态,调整动画效果(如低电量时简化动画)。
- 结合
AlarmManager
或Vibrator
实现唤醒设备的效果。 - 响应充电状态变化,触发特定动画(如充电时的波纹效果)。
权限要求
使用 PowerManager
的某些功能需要以下权限,在 AndroidManifest.xml
中声明:
<!-- 使用 WakeLock -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- 查询电池状态(可选) -->
<uses-permission android:name="android.permission.BATTERY_STATS" />
<!-- 检测设备重启(可选) -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- 动态权限:
WAKE_LOCK
和BATTERY_STATS
是普通权限,无需动态请求,用户安装应用时自动授予。 - 注意:API 23+(Android 6.0)引入 Doze 模式和 App Standby,可能限制
WakeLock
的行为;API 26+ 推荐使用前台服务替代长时间WakeLock
。
获取 PowerManager
通过 Context
获取 PowerManager
实例:
import android.content.Context;
import android.os.PowerManager;
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
常用功能与方法
以下是 PowerManager
的核心方法:
方法/功能 | 描述 | 权限要求 | API 级别 | 用法示例 |
---|---|---|---|---|
newWakeLock(int levelAndFlags, String tag) | 创建唤醒锁,控制屏幕或 CPU 状态。 | WAKE_LOCK | 1+ | PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "MyTag"); |
isInteractive() | 检查屏幕是否开启(API 20+)。 | 无 | 20+ | boolean isScreenOn = powerManager.isInteractive(); |
isPowerSaveMode() | 检查是否处于省电模式。 | 无 | 21+ | boolean isPowerSave = powerManager.isPowerSaveMode(); |
isDeviceIdleMode() | 检查是否处于 Doze 模式(API 23+)。 | 无 | 23+ | boolean isDoze = powerManager.isDeviceIdleMode(); |
reboot(String reason) | 重启设备(需系统权限)。 | 系统权限 | 8+ | powerManager.reboot("user"); |
isIgnoringBatteryOptimizations(String packageName) | 检查应用是否被电池优化忽略。 | 无 | 23+ | boolean ignored = powerManager.isIgnoringBatteryOptimizations(getPackageName()); |
- WakeLock 类型(
levelAndFlags
): PARTIAL_WAKE_LOCK
:保持 CPU 运行,屏幕和键盘可关闭。SCREEN_DIM_WAKE_LOCK
(API 17 弃用):屏幕保持低亮。SCREEN_BRIGHT_WAKE_LOCK
(API 17 弃用):屏幕保持高亮。FULL_WAKE_LOCK
(API 17 弃用):屏幕和键盘保持高亮。PROXIMITY_SCREEN_OFF_WAKE_LOCK
(API 21+):靠近传感器关闭屏幕。ACQUIRE_CAUSES_WAKEUP
:强制唤醒屏幕(与上述标志组合)。ON_AFTER_RELEASE
:释放后保持屏幕短暂开启。- 注意:API 17+ 推荐使用
FLAG_KEEP_SCREEN_ON
(通过 View 或 Window)替代旧版WakeLock
类型。
完整示例(WakeLock + 动画)
以下是一个使用 PowerManager
保持屏幕常亮并结合按钮动画的示例,触发 SoundPool
音效和振动。
import android.content.Context;
import android.media.AudioAttributes;
import android.media.SoundPool;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.widget.Button;
import android.widget.Toast;
import android.animation.ObjectAnimator;
import androidx.appcompat.app.AppCompatActivity;
public class PowerManagerActivity extends AppCompatActivity {
private PowerManager powerManager;
private PowerManager.WakeLock wakeLock;
private Vibrator vibrator;
private SoundPool soundPool;
private int soundId;
private Button actionButton;
private ObjectAnimator scaleAnimator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_power_manager);
actionButton = findViewById(R.id.action_button);
powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
// 初始化 SoundPool
soundPool = new SoundPool.Builder()
.setMaxStreams(1)
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build())
.build();
soundId = soundPool.load(this, R.raw.click_sound, 1);
// 初始化 WakeLock
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::WakeLock");
// 初始化动画
scaleAnimator = ObjectAnimator.ofFloat(actionButton, "scaleX", 1f, 1.2f, 1f);
scaleAnimator.setDuration(200);
// 按钮点击
actionButton.setOnClickListener(v -> {
// 获取 WakeLock
if (!wakeLock.isHeld()) {
wakeLock.acquire(10 * 60 * 1000L /* 10 分钟 */);
Toast.makeText(this, "WakeLock acquired", Toast.LENGTH_SHORT).show();
}
// 播放音效
soundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f);
// 触发振动
if (vibrator.hasVibrator()) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
} else {
vibrator.vibrate(100);
}
}
// 触发动画
scaleAnimator.start();
// 检查电源状态
boolean isPowerSave = powerManager.isPowerSaveMode();
Toast.makeText(this, "Power Save Mode: " + isPowerSave, Toast.LENGTH_SHORT).show();
});
}
@Override
protected void onPause() {
super.onPause();
if (wakeLock.isHeld()) {
wakeLock.release();
}
soundPool.autoPause();
scaleAnimator.cancel();
}
@Override
protected void onDestroy() {
super.onDestroy();
soundPool.release();
vibrator.cancel();
scaleAnimator.cancel();
}
}
- 布局 XML(res/layout/activity_power_manager.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Trigger Action" />
</LinearLayout>
- AndroidManifest.xml(添加权限):
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
- 说明:
- 权限:声明
WAKE_LOCK
和VIBRATE
权限,无需动态请求。 - WakeLock:使用
PARTIAL_WAKE_LOCK
保持 CPU 运行 10 分钟。 - 音效和振动:结合
SoundPool
和Vibrator
(前文介绍)增强交互。 - 动画:按钮点击时触发缩放动画(
scaleX
)。 - 电源状态:检查是否处于省电模式,显示提示。
- 生命周期:
onPause
释放WakeLock
和暂停音效,onDestroy
释放所有资源。
使用 FLAG_KEEP_SCREEN_ON
替代 WakeLock
,通过 Window
保持屏幕常亮:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- 移除屏幕常亮:
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- 优点:无需
WAKE_LOCK
权限,简单易用。 - 缺点:仅限 Activity 界面,无法控制 CPU。
检测电池状态
结合 BroadcastReceiver
监听电池状态:
BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level * 100 / (float) scale;
if (batteryPct < 20) {
scaleAnimator.cancel(); // 低电量停止动画
Toast.makeText(context, "Low battery: " + batteryPct + "%", Toast.LENGTH_SHORT).show();
}
}
};
registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
优缺点
- 优点:
- 提供灵活的电源管理(如
WakeLock
、屏幕控制)。 - 可检测省电模式和 Doze 状态,优化应用行为。
- 与动画、音效、振动结合增强用户体验。
- 缺点:
WakeLock
滥用可能增加耗电,API 26+ 推荐前台服务。- Doze 模式限制后台任务执行。
- 设备差异可能影响行为(如无振动硬件)。
- 替代方案:
- Foreground Service:API 26+ 替代长时间
WakeLock
。 - WorkManager:适合低优先级后台任务。
- BatteryManager:更详细的电池信息。
- WindowManager.FLAG_KEEP_SCREEN_ON:简单保持屏幕常亮。
注意事项
- 权限:确保声明
WAKE_LOCK
权限。 - WakeLock 管理:始终在适当时间释放
WakeLock
,避免耗电。 - Doze 模式:API 23+ 的 Doze 和 App Standby 可能限制
WakeLock
,使用前台服务或AlarmManager
。 - 生命周期:在
onPause
或onDestroy
释放资源(WakeLock
、动画、音效)。 - 电池优化:引导用户将应用加入电池优化白名单(
ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
)。 - 调试:通过 Log 检查
WakeLock
状态和电源模式。
扩展:Jetpack Compose 中的实现
在 Jetpack Compose 中,PowerManager
可结合 Composable 实现电源管理和动画:
import android.content.Context
import android.os.PowerManager
import android.os.VibrationEffect
import android.os.Vibrator
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
@Composable
fun PowerManagerScreen() {
val context = LocalContext.current
val powerManager = remember { context.getSystemService(Context.POWER_SERVICE) as PowerManager }
val vibrator = remember { context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator }
var wakeLock by remember { mutableStateOf<PowerManager.WakeLock?>(null) }
var isActive by remember { mutableStateOf(false) }
val scale by animateFloatAsState(
targetValue = if (isActive) 1.2f else 1f,
animationSpec = tween(200)
)
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("Power Save Mode: ${powerManager.isPowerSaveMode()}")
Spacer(Modifier.height(16.dp))
Button(
onClick = {
if (wakeLock == null || !wakeLock!!.isHeld) {
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::WakeLock")
wakeLock?.acquire(10 * 60 * 1000L) // 10 分钟
if (vibrator.hasVibrator()) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
vibrator.vibrate(100)
}
}
isActive = true
} else {
wakeLock?.release()
wakeLock = null
isActive = false
}
},
modifier = Modifier.scale(scale)
) {
Text(if (isActive) "Release WakeLock" else "Acquire WakeLock")
}
}
DisposableEffect(Unit) {
onDispose {
wakeLock?.release()
}
}
}
- 说明:
- 使用
remember
缓存PowerManager
和Vibrator
。 animateFloatAsState
实现按钮缩放动画。WakeLock
保持 CPU 运行 10 分钟,点击时触发振动。DisposableEffect
确保释放WakeLock
。- 显示当前省电模式状态。
示例:低电量检测
结合电池状态调整动画:
BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
if (level < 20) {
scaleAnimator.cancel(); // 低电量停止动画
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
}
};
registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
如果需要更复杂的功能(如 Doze 模式优化、与 AlarmManager
结合定时唤醒、或 Canvas 绘制电源状态动画),或想继续探讨 Android 动画合集(如过渡动画、Lottie),请告诉我!