Android电源锁和屏幕锁帮助类

2019-07-13 21:30发布

后台常驻服务, 使用了1像素Activity保活手段:
  • 在息屏时, 启动1像素Activity
  • 在亮屏时, 关闭1像素Activity
需要判断屏幕是否亮屏, 是否解锁. 特地写成一个帮助类. 首先AndroidMainFest中配置权限: <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.Manifest.permission.DEVICE_POWER"/> <uses-permission android:name="android.permission.GET_TASKS"/> 帮助类代码: public class UPowerManager { /** * 上下文 */ private Context context; /** * 回调接口 */ private ScreenStatusCallback callback; /** * 电源管理 */ private PowerManager pm; /** * 亮屏息屏操作对象 */ private PowerManager.WakeLock wakeLock; /** * 键盘锁管理器对象 */ private KeyguardManager km; /** * 屏幕监听器 */ private ScreenBroadcastReceiver receiver; /** * 当前使用的电源锁类型 */ private int curLevelAndFlags = -1; /** * @param context {@link Context} can not be null * @param callback {@link ScreenStatusCallback} can be null */ public UPowerManager(@NonNull Context context, @Nullable ScreenStatusCallback callback) { this.context = context; if (pm == null) { pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); } if (km == null) { km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); } //监听屏幕状态 if (callback != null) { this.callback = callback; registerReceiver(); } } /** * 注册屏幕监听广播 */ private void registerReceiver() { if (receiver == null) { receiver = new ScreenBroadcastReceiver(); } IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_USER_PRESENT); context.registerReceiver(receiver, filter); } /** * 息屏 * 传递的参数可以一个, 大部分可以组合使用(使用|隔开) * 与{@link UPowerManager#setScreenOn(int)} 成对使用 * * @param levelAndFlags {@link PowerManager#FULL_WAKE_LOCK} * {@link PowerManager#ON_AFTER_RELEASE} * {@link PowerManager#PARTIAL_WAKE_LOCK} * {@link PowerManager#SCREEN_DIM_WAKE_LOCK} * {@link PowerManager#ACQUIRE_CAUSES_WAKEUP} * {@link PowerManager#SCREEN_BRIGHT_WAKE_LOCK} * {@link PowerManager#PROXIMITY_SCREEN_OFF_WAKE_LOCK} * {@link PowerManager#RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY} */ public void setScreenOff(int levelAndFlags) throws ArgumentNotFormatLastException { if (curLevelAndFlags != -1) { if (curLevelAndFlags != levelAndFlags) { throw new ArgumentNotFormatLastException("与上一次传递进来的参数不一致"); } } curLevelAndFlags = levelAndFlags; if (wakeLock == null) { wakeLock = pm.newWakeLock(levelAndFlags, "Screen"); } wakeLock.acquire(); //wakeLock.acquire(2000); // 延迟2秒后获取电源锁 } /** * 亮屏 * 与{@link UPowerManager#setScreenOff(int)} 成对使用 * 传递的参数可以一个, 大部分可以组合使用(使用|隔开) * * @param levelAndFlags {@link PowerManager#FULL_WAKE_LOCK} * {@link PowerManager#ON_AFTER_RELEASE} * {@link PowerManager#PARTIAL_WAKE_LOCK} * {@link PowerManager#SCREEN_DIM_WAKE_LOCK} * {@link PowerManager#ACQUIRE_CAUSES_WAKEUP} * {@link PowerManager#SCREEN_BRIGHT_WAKE_LOCK} * {@link PowerManager#PROXIMITY_SCREEN_OFF_WAKE_LOCK} * {@link PowerManager#RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY} */ public void setScreenOn(int levelAndFlags) throws ArgumentNotFormatLastException { if (curLevelAndFlags != -1) { if (curLevelAndFlags != levelAndFlags) { throw new ArgumentNotFormatLastException("与上一次传递进来的参数不一致"); } } curLevelAndFlags = levelAndFlags; if (wakeLock == null) { wakeLock = pm.newWakeLock(levelAndFlags, "Screen"); } wakeLock.setReferenceCounted(false); wakeLock.release(); wakeLock = null; } /** * 息屏(靠近传感器) * 与{@link UPowerManager#setScreenOn()}成对使用 */ public void setScreenOff() { if (wakeLock == null) { wakeLock = pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "Screen"); } wakeLock.acquire(); } /** * 亮屏(远离传感器) * 与{@link UPowerManager#setScreenOff()}成对使用 */ public void setScreenOn() { if (wakeLock == null) { wakeLock = pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "Screen"); } wakeLock.setReferenceCounted(false); wakeLock.release(); wakeLock = null; } /** * 释放资源: 如果传入了回调接口, 必须调用此方法解绑广播 */ public void release() { if (context != null && receiver != null) { context.unregisterReceiver(receiver); } if (wakeLock != null) { wakeLock.release(); wakeLock = null; } } /** * 检查屏幕状态: * * @return true: 亮屏 false: 息屏 */ private boolean checkScreen() { return pm.isScreenOn(); } /** * 检查锁屏状态 * * @return true: 有锁屏, false: 无锁屏 */ private boolean isScreenLock() { return km.inKeyguardRestrictedInputMode(); } /** * 屏幕状态回调接口 * * @author tgvincent * @version 1.0 */ public interface ScreenStatusCallback { /** * 亮屏 */ void onScreenOn(); /** * 息屏 */ void onScreenOff(); /** * 解锁 */ void onUserPresent(); } /** * 屏幕状态广播接收器 * * @author tgvincent * @version 1.1 */ class ScreenBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { case Intent.ACTION_SCREEN_ON://开屏 callback.onScreenOn(); break; case Intent.ACTION_SCREEN_OFF://锁屏 callback.onScreenOff(); break; case Intent.ACTION_USER_PRESENT://解锁 callback.onUserPresent(); break; } } } /** * 自定义异常 */ public class ArgumentNotFormatLastException extends Exception { public ArgumentNotFormatLastException(String msg) { super(msg); } } } 上述代码中的电源锁效果各不相同, 具体如下:
  • PowerManager.FULL_WAKE_LOCK 保持 CPU 运转, 保持屏幕高亮显示, 键盘灯也保持亮度, 受电源键影响 应用场景: 来电话, 闹钟触发等
  • PowerManager.ON_AFTER_RELEASE 保持 CPU 运转, 保持屏幕高亮显示, 键盘灯也保持亮度. 特别说明: 和用户体验有关, 当wakelock释放后如果没有该标志, 屏幕会立即黑屏, 如果有该标志, 屏幕会亮一小会然后在黑屏. 不能和 PowerManager.PARTIAL_WAKE_LOCK 一起用
  • PowerManager.PARTIAL_WAKE_LOCK: 保持 CPU 运转, 屏幕和键盘灯有可能是关闭的. 最常用,保持CPU运转, 受到电源键影响. 不能和 PowerManager.ON_AFTER_RELEASE 或者 PowerManager.ACQUIRE_CAUSES_WAKEUP 一起用 应用场景: 听音乐, 后台下载等
  • PowerManager.SCREEN_DIM_WAKE_LOCK: 保持 CPU 运转, 允许保持屏幕显示但有可能是灰的, 允许关闭键盘灯, 受到电源键影响 应用场景: 即将进入灭屏休眠状态时
  • PowerManager.ACQUIRE_CAUSES_WAKEUP: 强制使屏幕亮起, 这种锁主要针对一些必须通知用户的操作. 说明: 正常情况下, 获取wakelock是不会唤醒设备的, 加上该标志之后, acquire wakelock也会唤醒设备, 该标志常用于闹钟触发, 蓝牙链接提醒等场景. 需要设备支持距离传感器, 不能和 PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK 或 PowerManager.PARTIAL_WAKE_LOCK 一起用
  • PowerManager.SCREEN_BRIGHT_WAKE_LOCK: 保持CPU 运转, 允许保持屏幕高亮显示, 允许关闭键盘灯, 受到电源键影响 应用场景: 看电子书, 看视频, 操作屏幕没有操作到键盘等
  • PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: 与传感器有关的一个电源锁, 获取此电源锁后, 靠近传感器息屏, 远离传感器亮屏. 受电源键影响 需要设备支持距离传感器, 不能和PowerManager.ACQUIRE_CAUSES_WAKEUP一起用
  • PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY 另一个与传感器有关的一个电源锁, 表示当传感器判断终端离物体较远时才真正释放PROXIMITY_SCREEN_OFF_WAKE_LOCK等级的电源锁
  • 其中: PowerManager.FULL_WAKE_LOCK PowerManager.SCREEN_DIM_WAKE_LOCK PowerManager.SCREEN_BRIGHT_WAKE_LOCK 这三种类型的锁, 在6.0以后版本会逐渐被抛弃使用, 改用WindowManager. LayoutParams 的一个参数FLAG_KEEP_SCREEN_ON 来替换上述三种类型的锁,因为它将由平台被准确地管理用户应用程序之间的动作, 并且不需要特殊的权限. 示例(保持当前Activity常量): public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } } 注意:
    一般不需要人为的去掉flag, WindowManager会管理好程序进入后台回到前台的操作. 如果确实需要手动清掉常亮的flag, 使用: getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 以上是普通APP可以使用的电源锁, 还有一些使用注解代码@hide隐藏起来的电源锁(主要是给系统别APP使用的), 略作介绍: PowerManager.DOZE_WAKE_LOCK: 低电状态, Doze模式下, 允许cpu进入suspend状态, 系统支持Doze模式 PowerManager.DRAW_WAKE_LOCK: 保持设备唤醒, 能正常进行绘图 ... **关于屏幕锁:** 当前的屏幕锁有五种, 分别是:
    1. 没有设置屏幕锁
    2. 滑动解锁
    3. 图案解锁
    4. PIN码解锁
    5. 密码解锁
    所以下面两个方法使用时要注意: /** * 检查屏幕状态: * * @return true: 亮屏 false: 息屏 */ private boolean checkScreen() { return pm.isScreenOn(); } /** * 检查锁屏状态 * * @return true: 有锁屏, false: 无锁屏 */ private boolean isScreenLock() { return km.inKeyguardRestrictedInputMode(); }
    • 如果没有设置屏幕锁, inKeyguardRestrictedInputMode() 返回值会一直为false.
    • 如果用户设置了屏幕锁(包括后四种锁中的任何一种)
      • 屏幕不亮时返回true
      • 屏幕亮时, 解锁前返回true
      • 解锁后返回false.
    有时候只需使用 inKeyguardRestrictedInputMode() 就可以了, 但当用户设置了屏幕锁的时候还需要配合isScreenOn() 方法一起使用!