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: 保持设备唤醒, 能正常进行绘图 ...
关于屏幕锁 当前的屏幕锁有五种,分别是:
- 没有设置屏幕锁
- 滑动解锁
- 图案解锁
- PIN码解锁
- 密码解锁
所以下面两个方法使用时要注意:
/**
* 检查屏幕状态:
*
* @return true: 亮屏 false: 息屏
*/
private boolean checkScreen() {
return pm.isScreenOn();
}
/**
* 检查锁屏状态
*
* @return true: 有锁屏, false: 无锁屏
*/
private boolean isScreenLock() {
return km.inKeyguardRestrictedInputMode();
}
-
如果没有设置屏幕锁,
inKeyguardRestrictedInputMode()
返回值会一直为false -
如果用户设置了屏幕锁(包括后四种锁中的任何一种)
(1)屏幕不亮时返回true
(2)屏幕亮时,解锁前返回true,解锁后返回false
有时只需使用 inKeyguardRestrictedInputMode()
即可,但当用户设置了屏幕锁时还需要配合 isScreenOn()
方法一起使用!
版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/02/26/android-power-lock-and-screen-lock-help-class/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论