背景
近期项目的迭代版本开发,部门惊喜的申请了一台9.0的机器,是目前部门有史以来第一台8.0以上的机器,满怀喜悦的跑起项目,惊讶地发现Notification的在9.0以上的机器突然不能弹出通知了,惊讶之余发现发通知管理的权限没有开启(就觉得在我的代码怎么会有问题),结果开启了仍然无法接收到通知(打脸了...),马上请教了google大神,发现了毛病
问题
·Android O上发不出来通知了
·设置通知的震动、声音、呼吸灯都不起作用
问题一
从源码入手
查看 NotificationManagerService.java -> enqueueNotificationInternal()
发现channel空了
String channelId = notification.getChannelId();
final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
notificationUid, channelId, false /* includeDeleted */);
if (channel == null) {
final String noChannelStr = "No Channel found for "
+ "pkg=" + pkg
+ ", channelId=" + channelId
+ ", id=" + id
+ ", tag=" + tag
+ ", opPkg=" + opPkg
+ ", callingUid=" + callingUid
+ ", userId=" + userId
+ ", incomingUserId=" + incomingUserId
+ ", notificationUid=" + notificationUid
+ ", notification=" + notification;
Log.e(TAG, noChannelStr);
doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
"Failed to post notification on channel \"" + channelId + "\"\n" +
"See log for more details");
return;
}
查看 RankingHelper.java -> createDefaultChannelIfNeeded()
private boolean shouldHaveDefaultChannel(Record r) throws NameNotFoundException {
final int userId = UserHandle.getUserId(r.uid);
final ApplicationInfo applicationInfo = mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
// O apps should not have the default channel.
return false;
}
// Otherwise, this app should have the default channel.
return true;
}
上面的源码可以看出:
当 targetSdk >= 26 时,系统是不会给你添加默认Channel的,反之低版本则会默认添加;
即使是targetSdk < 26,只要你的 compileSdk >= 26 ,也是可以设置Channel的,同样也会生效。
问题二
照旧看一下源码,因为用到了Channel这个东西,直接查看NotificationChannel源码,你会发现这个是继承Parcelable的一个实体类, 这个类就是用于存放通知的一些属性信息,比如提示音,知识灯,震动等
因为O之前是系统默认创建Channel,会将Builder的里面的属性都添加,如下:
Notification.Builder notification = new Notification
.Builder(applicationContext)
.setContentTitle("您有一条新消息")
.setContentText(title)
.setWhen(System.currentTimeMillis())
.setSmallIcon(idIco)
.setLargeIcon(BitmapFactory.decodeResource(applicationContext.getResources(), idIco))
.setVibrate(vibrate)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(Notification.DEFAULT_ALL)
O之后就需要我们自己在自己创建的Channel中添加,然后将Channel对象传给NotificationManager去处理
// 传入参数:通道ID,通道名字,通道优先级(类似曾经的 builder.setPriority())
NotificationChannel channel =
new NotificationChannel(NOTIFICATION_CHANNELID, name, NotificationManager.IMPORTANCE_HIGH);
// 配置通知渠道的属性
channel.setDescription(description);
// 设置通知出现时声音,默认通知是有声音的
channel.setSound(null, null);
// 设置通知出现时的闪灯(如果 android 设备支持的话)
channel.enableLights(true);
channel.setLightColor(Color.RED);
// 设置通知出现时的震动(如果 android 设备支持的话)
channel.enableVibration(true);
channel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
//最后在 notificationManager 中创建该通知渠道
mNotificationManager.createNotificationChannel(channel);
这样一来声音和震动等问题都解决了
完整的代码例子:
public void createNotification(Context context, NotifyEntity entity) {
String id = context.getPackageName();
String title = DEFAULT_CHANNEL_NAME;
Intent intent = getIntent(context, entity);
PendingIntent pendingIntent;
NotificationCompat.Builder builder;
NotificationManager manager = (NotificationManager) context.getSystemService(
Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel mChannel = manager.getNotificationChannel(id);
if (mChannel == null) {
mChannel = new NotificationChannel(id, title, importance);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 });
manager.createNotificationChannel(mChannel);
}
builder = new NotificationCompat.Builder(context, id);
pendingIntent = PendingIntent.getActivity(context, JPushUtils.code, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
builder.setContentTitle(entity.getTitle())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(getLargeBitmap(context))
.setContentText(entity.getContent())
.setDefaults(Notification.DEFAULT_ALL)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setTicker(entity.getTitle())
.setVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 });
} else {
builder = new NotificationCompat.Builder(context);
pendingIntent = PendingIntent.getActivity(context, JPushUtils.code, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
builder.setContentTitle(entity.getTitle())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(getLargeBitmap(context))
.setContentText(entity.getContent())
.setDefaults(Notification.DEFAULT_ALL)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setTicker(entity.getTitle())
.setVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })
.setPriority(Notification.PRIORITY_HIGH);
}
Notification notification = builder.build();
manager.notify(JPushUtils.code + 1, notification);
}
private Intent getIntent(Context context, NotifyEntity entity) {
Intent intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
Log.i(TAG, "entity.getExtra() = " + entity.getExtra());
Bundle bundle = new Bundle();
if (entity.getExtra() != null) {
bundle.putString(AppConstants.KEY_GAME_ID, entity.getExtra().getData());
bundle.putString(AppConstants.KEY_TYPE, entity.getExtra().getType());
intent.putExtras(bundle);
}
return intent;
}
private Bitmap getLargeBitmap(Context context) {
Drawable drawable = ContextCompat.getDrawable(context,
R.mipmap.ic_launcher);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
return bitmap;
}
以上是我的一些个人总结和归纳,欢迎大家点评和指导,如果有错麻烦指出
参考:
https://www.jianshu.com/p/99bc32cd8ad6
https://blog.csdn.net/weixin_33698043/article/details/90858968