Android电源锁和屏幕锁帮助类

后台常驻服务, 使用了1像素Activity保活手段:

  • 在息屏时, 启动1像素Activity
  • 在亮屏时, 关闭1像素Activity
需要判断屏幕是否亮屏, 是否解锁. 特地写成一个帮助类. 首先AndroidMainFest中配置权限:
<!--息屏亮屏1 -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!--息屏亮屏2 -->
<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() 方法一起使用!
    • 1
      点赞
    • 2
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值