目标
分析AOSP中关于通知排序的代码,了解Android通知排序机制。
分析
NotificationRecord排序的代码位于NotificationComparator:
// frameworks/base/services/core/java/com/android/server/notification/NotificationComparator.java
@Override
public int compare(NotificationRecord left, NotificationRecord right) {
final int leftImportance = left.getImportance();
final int rightImportance = right.getImportance();
final boolean isLeftHighImportance = leftImportance >= IMPORTANCE_DEFAULT;
final boolean isRightHighImportance = rightImportance >= IMPORTANCE_DEFAULT;
if (isLeftHighImportance != isRightHighImportance) {
// by importance bucket, high importance higher than low importance
return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance);
}
// If a score has been assigned by notification assistant service, use this service
// rank results within each bucket instead of this comparator implementation.
if (left.getRankingScore() != right.getRankingScore()) {
return -1 * Float.compare(left.getRankingScore(), right.getRankingScore());
}
// first all colorized notifications
boolean leftImportantColorized = isImportantColorized(left);
boolean rightImportantColorized = isImportantColorized(right);
if (leftImportantColorized != rightImportantColorized) {
return -1 * Boolean.compare(leftImportantColorized, rightImportantColorized);
}
// sufficiently important ongoing notifications of certain categories
boolean leftImportantOngoing = isImportantOngoing(left);
boolean rightImportantOngoing = isImportantOngoing(right);
if (leftImportantOngoing != rightImportantOngoing) {
// by ongoing, ongoing higher than non-ongoing
return -1 * Boolean.compare(leftImportantOngoing, rightImportantOngoing);
}
boolean leftMessaging = isImportantMessaging(left);
boolean rightMessaging = isImportantMessaging(right);
if (leftMessaging != rightMessaging) {
return -1 * Boolean.compare(leftMessaging, rightMessaging);
}
// Next: sufficiently import person to person communication
boolean leftPeople = isImportantPeople(left);
boolean rightPeople = isImportantPeople(right);
final int contactAffinityComparison =
Float.compare(left.getContactAffinity(), right.getContactAffinity());
if (leftPeople && rightPeople){
// by contact proximity, close to far. if same proximity, check further fields.
if (contactAffinityComparison != 0) {
return -1 * contactAffinityComparison;
}
} else if (leftPeople != rightPeople) {
// People, messaging higher than non-messaging
return -1 * Boolean.compare(leftPeople, rightPeople);
}
boolean leftSystemMax = isSystemMax(left);
boolean rightSystemMax = isSystemMax(right);
if (leftSystemMax != rightSystemMax) {
return -1 * Boolean.compare(leftSystemMax, rightSystemMax);
}
if (leftImportance != rightImportance) {
// by importance, high to low
return -1 * Integer.compare(leftImportance, rightImportance);
}
// by contact proximity, close to far. if same proximity, check further fields.
if (contactAffinityComparison != 0) {
return -1 * contactAffinityComparison;
}
// Whether or not the notification can bypass DND.
final int leftPackagePriority = left.getPackagePriority();
final int rightPackagePriority = right.getPackagePriority();
if (leftPackagePriority != rightPackagePriority) {
// by priority, high to low
return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
}
final int leftPriority = left.getSbn().getNotification().priority;
final int rightPriority = right.getSbn().getNotification().priority;
if (leftPriority != rightPriority) {
// by priority, high to low
return -1 * Integer.compare(leftPriority, rightPriority);
}
// then break ties by time, most recent first
return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
}
每个判断条件的解释如下:
- importance:如果一个大于等于default,一个小于default,则按importance排序;
- getRankingScore:这个从applyAdjustments中过来,应用不能控制;
- isImportantColorized:这个需要android.permission.USE_COLORIZED_NOTIFICATIONS权限,普通应用无法申请;
- isImportantOngoing:应用可配置;
- isImportantMessaging:这个必须满足isDefaultMessagingApp,即系统默认短信应用,需用户主动配置;
- isImportantPeople:和通讯录设置有关,非应用干预;
- isSystemMax:包名必须是android或者com.android.systemui;
- getPackagePriority:取决于canBypassDnd,即该应用是否可绕过Do Not Disturb;
- priority:应用可配置;
- getRankingTimeMs:时间,可由开发者决定但是不能超过通知发出的时间,如下所示。
提升通知优先级
通知发出时间
一种比较流行的方法是设置通知时间为未来时间:
public void postNotificationTop() {
NotificationManager notificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel("channel_id",
"channel_name", NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channel_id")
.setContentTitle("Title")
.setContentText("text")
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setWhen(System.currentTimeMillis() + 10000)
.setShowWhen(false)
.setAutoCancel(true);
notificationManager.notify(0, builder.build());
}
setWhen设置时间为当前时间+10000毫秒,然后setShowWhen为false。然而实际上这样设置并不起作用,因为代码中判断了不能超过通知发出时间,所以实际还是通知优先级决定了排序。
// frameworks/base/services/core/java/com/android/server/notification/NotificationRecord.java
/**
* @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()}
* of the previous notification record, 0 otherwise
*/
private long calculateRankingTimeMs(long previousRankingTimeMs) {
Notification n = getNotification();
// Take developer provided 'when', unless it's in the future.
if (n.when != 0 && n.when <= getSbn().getPostTime()) {
return n.when;
}
// If we've ranked a previous instance with a timestamp, inherit it. This case is
// important in order to have ranking stability for updating notifications.
if (previousRankingTimeMs > 0) {
return previousRankingTimeMs;
}
return getSbn().getPostTime();
}
Ongoing通知
Ongoing的方式是可以的,配置setOngoing(true)
即可:
public void postNotificationTop() {
NotificationManager notificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel("capcut_top",
"capcut top", NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "capcut_top")
.setContentTitle("Capcut")
.setContentText("置顶消息测试")
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setOngoing(true)
.setShowWhen(false)
.setAutoCancel(true);
notificationManager.notify(0, builder.build());
}
配置mBypassDnd=true
在getPackagePriority的比较中,实际比较的是mPackagePriority:
public int getPackagePriority() {
return mPackagePriority;
}
而setPackagePriority又是决定于NotificationChannel的canBypassDnd:
public RankingReconsideration process(NotificationRecord record) {
if (record == null || record.getNotification() == null) {
if (DBG) Slog.d(TAG, "skipping empty notification");
return null;
}
if (mConfig == null) {
if (DBG) Slog.d(TAG, "missing config");
return null;
}
record.setPackagePriority(record.getChannel().canBypassDnd()
? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT);
return null;
}
最后canBypassDnd是决定于mBypassDnd变量:
/**
* Whether or not notifications posted to this channel can bypass the Do Not Disturb
* {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode.
*/
public boolean canBypassDnd() {
return mBypassDnd;
}
可以通过setBypassDnd来设置
channel.setBypassDnd(true);