安卓的通知适配(更新至9.0)

以下代码以compileVersion=28作为示例来演示

  • 添加NotificationChannel(必需)

    • compileVersion>=26且Notification没有设置channelId时,8.0的系统上通知不会弹出,在logcat的error级别显示NotificationService提示日志:No Channel found for pkg=aaa, channelId=null,...notification=Notification(channel=null ...)

    • 可以使用两种方式给Notification对象添加channelId: NotificationCompat.Builder(context, channelId)...build()或者build.setChannelId(channelId)...

    • NotificationChannel创建: NotificationChannel(String channelId, CharSequence name, @Importance int importance)

      • channelIdbuild时设置给Notification的id
      • name显示在通知管理里的标题
      • importance此通道的重要性,5个等级范围
    • 在通知被notify(notification)之前必须确保通知的NotificationChannel已经被注册,api: createNotificationChannel(channel)

  • 机型适配

    • 有的手机在添加channel后仍然无法弹出通知。追踪logcat发现有这么一句:

      E/NotificationService: Suppressing notification from package by user request.
      

      用户请求抑制此通知?追踪notify源码找到NotificationManagerServiceenqueueNotificationInternal(......)方法:

      void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,final int callingPid, final String tag, final int id, final Notification notification,int incomingUserId) {
       ...
       if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r)) {return;}
       ...
       mHandler.post(new EnqueueNotificationRunnable(userId, r));
      }
      private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,NotificationRecord r) {
        ...// blocked apps
        if (isBlocked(r, mUsageStats)) {return false;}
        return true;
      }
      protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
        final String pkg = r.sbn.getPackageName();
        final int callingUid = r.sbn.getUid();
        final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
        if (isPackageSuspended) {
            Slog.e(TAG, "Suppressing notification from package due to package suspended by administrator.");
            usageStats.registerSuspendedByAdmin(r);
            return isPackageSuspended;
        }
        
        final boolean isBlocked = mRankingHelper.getImportance(pkg, callingUid) == NotificationManager.IMPORTANCE_NONE
                    || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
        if (isBlocked) {
            Slog.e(TAG, "Suppressing notification from package by user request.");
            usageStats.registerBlocked(r);
        }
        return isBlocked;
      }
      

      最终的isBlocked判断条件满足,导致notify操作被中断return。

    • 目前为止国产rom现状是:

      • 通知总权限在华为EMUI/魅族flyme/原生系统上默认是打开的,MIUI/VIVO/OPPO是默认关闭的
      • 渠道开关在OPPO手机上是默认关闭的,在开启总权限后还需要开启相关的类别(对应channel的name)才能正常使用。而测试的其他手机在开启总开关后自动开启channelId的通知开关。
        安卓的通知适配(更新至9.0)_第1张图片
    • 这么检测通知权限:

      public static boolean isNotificationEnabled(Context context,String channelId) {
          NotificationManagerCompat managerCompat = NotificationManagerCompat.from(context);
          NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
          boolean returnValue = managerCompat.areNotificationsEnabled();
          if(manager == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.O){
              return returnValue;
          }
          NotificationChannel channel = manager.getNotificationChannel(channelId);
          if(channel == null){
              channel = new NotificationChannel(channelId,"我的推送类别",NotificationManager.IMPORTANCE_HIGH);
              manager.createNotificationChannel(channel);
      
              下面的获取操作必需,创建的channel和获取到的channel的IMPORTANCE可能不一样,OPPO默认IMPORTANCE_NONE。
              channel = manager.getNotificationChannel(channelId);
          }
          return returnValue && channel.getImportance() != NotificationManager.IMPORTANCE_NONE;
      }
      
    • 跳转通知管理页面的代码:

      boolean isNotifyEnable = NotificationManagerCompat.from(context).areNotificationsEnabled();
      boolean isChannelEnable = true;
      if (Build.VERSION.SDK_INT >= 26) {
          isChannelEnable = channel.getImportance() != NotificationManager.IMPORTANCE_NONE;
      }
      if (isNotifyEnable && isChannelEnable) {
          manager.notify(notifyId, notification);  正常notify
      } else if (!isNotifyEnable) {
          Intent intent = new Intent();
          if(!(context instanceOf Activity)){
              intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          }
          if (Build.VERSION.SDK_INT >= 26) {
              intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
              context.startActivity(intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()));
          }else if (Build.VERSION.SDK_INT >= 21) {
              intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
              context.startActivity(intent.putExtra("android.provider.extra.APP_PACKAGE", getPackageName()));
          } else if (Build.VERSION.SDK_INT >= 9) {
              intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
              intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
              intent.setData(Uri.fromParts("package", getPackageName(), null));
              context.startActivity(intent);
          } else {低于9没有适配必要}
      }else{
         只打开了通知开关,但是关闭了当前channel的通知,开发者需要根据通知重要性,自行决定如何提示用户
      }
      
      • 查看api21与api26的源码发现Settings. ACTION_APP_NOTIFICATION_SETTINGS 的值其实就是 "android.settings. APP_NOTIFICATION_SETTINGS",只是在26前这个常量是隐藏的。因此上述代码可简化掉api26那部分。
      • 创建的NotificationChannel和notify时获取的NotificationChannel可能是不同的。因为在service层保存的实现方法国产rom做了更改。以防万一,请使用manager.getNotificationChannel(channelId)生成的NotificationChannel对象
      • 测试时发现vivo X21有两个通知管理页面:
        安卓的通知适配(更新至9.0)_第2张图片
        右边明显很漂酿啊有木有
        右侧的设计很好看,使用shell 命令dumpsys activity | grep -i run找到落地页(包名:com.android.systemui Activity名:com.vivo.systemui.statusbar.notification.settings.channels.NotificationSettingsActivity) 跑了下崩了,提示Permission Denial: starting Intent{...}not exported from uid 10023。activity没有设置为exported,有空了看看源码是否有破解方法...
  • CompileSdkVersion<26时的机型适配

    • NotificationChannel这个API是在安卓8.0引入,所以当编译版本低于26时,不能加入channel,但是经过测试在vivo的安卓8.0手机上提示Suppressing notification from package by user request 通知无法弹出
    • areNotificationsEnabled 在安卓7.0(api24)加入,对应的support支持包最低v7:24.0.0。如果编译版本不低于api24,做如下适配:
      boolean isNotifyEnable = NotificationManagerCompat.from(this).areNotificationsEnabled();
      if(isNotifyEnable){
          manager.notify(notifyId,notification);
      }else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
          startActivity(new Intent("android.settings.APP_NOTIFICATION_SETTINGS")
                .putExtra("android.provider.extra.APP_PACKAGE", getPackageName()));
      } else/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) */{
          Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
          intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          intent.setData(Uri.fromParts("package", getPackageName(), null));
          startActivity(intent);
      }   考虑到目前app没人再适配android 2.x,因此最后一个else省略
      
    • 如果编译版本低于24,可参考高版本api自行实现,这里就不贴出了。目前各应用商店都在强制提升targetVersion,以后如果是上应用市场的app不会再有低于26的编译版本了

你可能感兴趣的:(安卓的通知适配(更新至9.0))