Android电源锁和屏幕锁帮助类

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

  • 在息屏时,启动1像素Activity
  • 在亮屏时,关闭1像素Activity

需要判断屏幕是否亮屏,是否解锁,特地写成一个帮助类。首先AndroidMainFest中配置权限

1
2
3
4
5
6
<!--息屏亮屏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"/>

帮助类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
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_LOCKPowerManager.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常亮):

1
2
3
4
5
6
7
8
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,使用

1
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. 密码解锁

所以下面两个方法使用时要注意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 检查屏幕状态:
*
* @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() 方法一起使用!

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :