最近碰到一个这样的需求,说是要收到任何消息后若手机处在锁屏状态时要自动亮屏,开始拿到这个需求后有点蒙,对于一个小安卓的我来说,感觉根本无从下手呀,但是客户是上帝(其实就是经理是上帝),没办法,身为底层员工的我就只能硬着头皮上了。先从需求上分析吧,既然是要自动亮屏,那这个操作肯定是跟亮屏有关,于是赶紧去查询亮屏有关的api,在逛了很久的CSDN后,发现亮屏的操作与PowerManagerService.java这个类有关也看了下大神们对这个类的剖析,有兴趣的可以去逛逛大神的博客:https://www.jianshu.com/p/9932134a5b42, 这里我就不去赘述了,反正具体点亮屏幕的代码如下:
PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock((PowerManager.ACQUIRE_CAUSES_WAKEUP
| PowerManager.SCREEN_BRIGHT_WAKE_LOCK), "Notification");
wl.acquire();
那么找到了亮屏的操作,现在就是要分析把这段代码丢在哪才能满足需求了,客户要求是收到任何消息后手机都要亮屏,那么显然在单一应用或者在单一发起显示通知的地方加上这段代码是不可行的,那么要满足这个需求,就必须在framework层的消息处理这一块动代码了(想想都有点小激动呢),既然有了思路,那么就赶紧行动吧
我们知道,在我们构建好了notification后会调用NotificationManager的notify()方法来发起通知,那么我们就来看看这个方法的具体实现吧:
路径:frameworks/base/core/java/android/app/NotificationManager.java
public void notify(String tag, int id, Notification notification)
{
int[] idOut = new int[1];
INotificationManager service = getService();
String pkg = mContext.getPackageName();
.....
.....
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
stripped, idOut, UserHandle.myUserId());
if (id != idOut[0]) {
Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}
} catch (RemoteException e) {
}
}
从上面可以看出,其实NotificationManager并没有做什么操作,最后面还是调用NotificationManagerService中的enqueueNotificationWithTag()方法
那我们继续看看NotificationManagerService里的这个方法,路径:frameworks/base/services/java/com/android/server/NotificationManagerService.java
public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Notification notification, int[] idOut, int userId) throws RemoteException {
enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Binder.getCallingPid(), tag, id, notification, idOut, userId);
}
可以看到,真正实现的方法是enqueueNotificationInternal()这个方法,那继续往下:
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
final int callingPid, final String tag, final int id, final Notification notification,
int[] idOut, int incomingUserId) {
....
if(tag!=null&&!tag.equals("DownloadNotificationService")){
//modify wakeup lcd if has new notifications
PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock((PowerManager.ACQUIRE_CAUSES_WAKEUP
| PowerManager.SCREEN_BRIGHT_WAKE_LOCK), "Notification");
wl.acquire();
}
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mNotificationList) {
...
// blocked apps
//如果用户设置了该引用不显示通知,并且不是系统通知的话,直接将该通知打分为-1000
if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
if (!isSystemNotification) {
//JUNK_SCORE = -1000;
r.score = JUNK_SCORE;
}
}
//SCORE_DISPLAY_THRESHOLD = -20;
//打分小于阈值的通知不显示
if (r.score < SCORE_DISPLAY_THRESHOLD) {
// Notification will be blocked because the score is too low.
return;
}
//垃圾通知,也不会显示
if (isNotificationSpam(notification, pkg)) {
mArchive.record(r.sbn);
return;
}
...
//只显示有图标的通知
if (notification.icon != 0) {
StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
mListeners.notifyPostedLocked(n, oldSbn);
}
...
//声音,震动,闪光灯的控制
buzzBeepBlinkLocked(r);
}
}
});
}
}
真正处理消息的是那个handle方法里面了,那么到这里为止我们就能清晰的知道所有的消息都会通过enqueueNotificationInternal()这个方法来处理,所以我们只要在这个方法中加上上面的代码就能达到效果了,哈哈,是不是很开心.如下就是我添加的代码:
PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock((PowerManager.ACQUIRE_CAUSES_WAKEUP
| PowerManager.SCREEN_BRIGHT_WAKE_LOCK), "Notification");
wl.acquire();
代码好了后就是就是编译了,当我屁颠屁颠的把代码编译后烧入手机,发现确实做到了需求要去的功能,瞬间优越感油然而生,但是还没高兴一回儿就发现有一个bug,就是当手机处于下载时,锁屏状态会一直亮屏,好尴尬,因为下载状态那个进度条也是个消息,所以自然会走这个逻辑,更可气的是下载进度条会一直更新,所以会一直发送消息,从而导致一直亮屏;这让我傻眼了,那么有什么方法可以规避这个bug不,我想的是加个判断,若是下载的消息就不执行这个亮屏操作,嗯,就是这样,那么怎么才能知道是下载状态呢,通过log我发现当下载时发送的通知有一个tag标志为:DownloadNotificationService,所以就可以按照这个加判断,具体如下:
if(tag!=null&&!tag.equals("DownloadNotificationService")){
//modify wakeup lcd if has new notifications
PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock((PowerManager.ACQUIRE_CAUSES_WAKEUP
| PowerManager.SCREEN_BRIGHT_WAKE_LOCK), "Notification");
wl.acquire();
}
重新编译后发现终于成功了,哈哈,到此,功能终于完成了,虽然可能还有其他的问题,但是俗话说,兵来将挡,水来土掩,总会有解决方法的,关键还是要找对思路,好了,希望以上能对大家有帮助,谢谢。