通知概览https://developer.android.google.cn/guide/topics/ui/notifiers/notifications
一、悬浮通知创建
简单通知创建代码
public void showAlarmNotification() {
//IMPORTANCE_HIGH 重要通知,弹出悬浮通知
NotificationChannel channel = new NotificationChannel("123", "测试 channel_name", NotificationManager.IMPORTANCE_HIGH);
//led灯
channel.enableLights(true);
//锁屏显示通知
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
//led灯颜色
channel.setLightColor(Color.BLUE);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.baidu.com"));
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
// Get the layouts to use in the custom notification
RemoteViews notificationLayout = new RemoteViews(getPackageName(), R.layout.notification_layout);
notificationLayout.setTextViewText(R.id.text, "contentView");
RemoteViews notificationLayoutExpanded = new RemoteViews(getPackageName(), R.layout.notification_layout_big);
notificationLayoutExpanded.setTextViewText(R.id.text, "bigContentView");
// Apply the layouts to the notification
Notification customNotification = new NotificationCompat.Builder(this, "123")
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("imageTitle")
.setContentText("imageDescription")
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.ic_android_black_24dp))
.setStyle(new NotificationCompat.BigPictureStyle()
.bigPicture(BitmapFactory.decodeResource(getResources(),R.drawable.ic_android_black_24dp))
.bigLargeIcon(null))
.setFullScreenIntent(pendingIntent, false)
.build();
notificationManager.notify(0, customNotification);
}
悬浮通知,就是在创建 NotificationChannel 时指定等级为 IMPORTANCE_HIGH 。系统对通知定义等级六级:
// 效果等于无通知 public static final int IMPORTANCE_NONE = 0; // 通知被折叠起来,不在状态栏显示 public static final int IMPORTANCE_MIN = 1; // 在状态栏显示,但是没有提示音等 public static final int IMPORTANCE_LOW = 2; // 在状态栏显示,有提示音等,但没有弹出悬浮框 public static final int IMPORTANCE_DEFAULT = 3; // 在状态栏显示,有提示音等,有弹出悬浮框 public static final int IMPORTANCE_HIGH = 4; // 效果等同于 IMPORTANCE_HIGH public static final int IMPORTANCE_MAX = 5;
二、悬浮通知上滑后再次创建不弹出问题
调试时候会遇到,点击APP希望弹出悬浮通知时,没有弹出场景。这个可能是在之前一分钟内,有过弹出悬浮通知,且被我们自己上滑取消。如此,接下来一分钟内,不再弹出通知。
这是系统的默认设计,系统在用户手动上滑时,会认为用户此时有其他活动,弹出的通知栏可能干扰了用户,所以用户上滑隐藏通知,故短时间内可能我们不应该再弹出相同通道的通知。
这部分控制在 NotificationInterruptStateProviderImpl.java 中
public boolean shouldHeadsUp(NotificationEntry entry) {
if (mStatusBarStateController.isDozing()) {
return shouldHeadsUpWhenDozing(entry);
} else {
return shouldHeadsUpWhenAwake(entry);
}
}
... ...
private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) {
StatusBarNotification sbn = entry.getSbn();
// 与 Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED 值相关
if (!mUseHeadsUp) {
if (DEBUG_HEADS_UP) {
Log.d(TAG, "No heads up: no huns");
}
return false;
}
if (!canAlertCommon(entry)) {
return false;
}
if (!canAlertAwakeCommon(entry)) {
return false;
}
if (isSnoozedPackage(sbn)) {
if (DEBUG_HEADS_UP) {
Log.d(TAG, "No alerting: snoozed package: " + sbn.getKey());
}
return false;
}
... ...
private boolean isSnoozedPackage(StatusBarNotification sbn) {
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
shouldHeadsUp
此函数判断是否有悬浮通知,正常状态下执行。
shouldHeadsUpWhenAwake
在这里对即将执行的应用通知的各种状态进行检查。上滑后,会触发通知进入 Snooze 状态,会被return false ,判断不可弹出悬浮框。判断条件如下:
isSnoozedPackage(sbn) -> mHeadsUpManager.isSnoozed(sbn.getPackageName())
mHeadsUpManager 对象类 HeadsUpManager.java 主要用于管理悬浮通知状态的,管理
/**
* 判断通知是否处于 Snoozes 状态
*/
public boolean isSnoozed(@NonNull String packageName) {
final String key = snoozeKey(packageName, mUser);
Long snoozedUntil = mSnoozedPackages.get(key);
if (snoozedUntil != null) {
if (snoozedUntil > mClock.currentTimeMillis()) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, key + " snoozed");
}
return true;
}
mSnoozedPackages.remove(packageName);
}
return false;
}
/**
* 增加 Snoozes 状态通知
*/
public void snooze() {
for (String key : mAlertEntries.keySet()) {
AlertEntry entry = getHeadsUpEntry(key);
String packageName = entry.mEntry.getSbn().getPackageName();
mSnoozedPackages.put(snoozeKey(packageName, mUser),
mClock.currentTimeMillis() + mSnoozeLengthMs);
}
}
通知上滑会执行 snooze() ,将当前通知包名添加到 mSnoozedPackages 中。后续该包名通知发出时,isSnoozed 中检查 mSnoozedPackages 是否存在此发出通知应用的包名。存在时,判断 snoozedUntil 是否已经到达当前时间。
snoozedUntil == mClock.currentTimeMillis() + mSnoozeLengthMs
mSnoozeLengthMs 默认是1分钟,所以一般设备上,通知上滑1分钟后,才有悬浮通知效果。
int defaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
修改 mSnoozeLengthMs 默认值,只要修改 heads_up_default_snooze_length_ms 即可,或者动态修改,修改 SETTING_HEADS_UP_SNOOZE_LENGTH_MS 属性。