Android Notification细思极恐的适配

背景

近期项目的迭代版本开发,部门惊喜的申请了一台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的一个实体类, 这个类就是用于存放通知的一些属性信息,比如提示音,知识灯,震动等


image.png

因为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

你可能感兴趣的:(Android Notification细思极恐的适配)