美文网首页
notification的适配

notification的适配

作者: 鹈鹕醍醐 | 来源:发表于2019-02-20 19:04 被阅读17次
  • 1:添加NotificationChannel

    • 1.1:Notification如果没有设置channelId,通知不会弹出,在logcat的error级别显示NotificationService提示日志:No Channel found for pkg=aaa, channelId=null,...notification=Notification(channel=null ...)

    • 1.2:可见在安卓8.0及以上通知是必须配置ChannelId的。有两种方式给Notification对象添加channelId:

      • 1.2.1:Notification notify = new NotificationCompat.Builder(this, channelId)...
      • 1.2.2:NotificationCompat.Builder build.setChannelId(channelId)...
    • 1.3:NotificationChannel的创建与注册

      • public NotificationChannel(String id, CharSequence name, @Importance int importance)
      • idchannelId
      • name显示在通知管理里的标题
      • importance代表此通道的重要性,从高到低限定了5个等级范围,有不同的表现行为
    • NotificationManager#notify()前需要确保channelId已经被创建过: NotificationManager#createNotificationChannel(NotificationChannel);

  • 2:targetSdkVersion>=26时的机型适配

    • 2.1: 在实际开发中发现:即使按照官方规范添加了NotificationChannel,好多手机上依然无法弹出通知。追踪logcat发现有这么一句:

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

      用户请求抑制此通知?追查源码NotificationManager#notify(int id, Notification notification) 最终调用了NotificationManagerService#enqueueNotificationInternal(......)

      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。目前为止:通知权限在华为EMUI/魅族flyme/原生系统上默认是打开的,MIUI/VIVO/OPPO是默认关闭的,好吧为了通知能够正常使用先上一段权限工具代码试试:

      boolean enabled = NotificationManagerCompat.from(context).areNotificationsEnabled();
      if (enabled) {
          notificationManager.notify(notifyId, notification);
      } else {
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
              startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
                      .putExtra(Settings.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);
          } else {
              Intent intent = new Intent(Intent.ACTION_VIEW);
              intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
              intent.setClassName("com.android.settings","com.android.settings.InstalledAppDetails");
              intent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());
              startActivity(intent);
          }
      }
      
    • 2.2: 事情还没结束:在oppo手机上发现即使引导用户打开了通知开关但是单个channel默认还是关闭的,需要两个开关都开启才能正常使用。而其他手机在开启通知时自动开启channelId的通知开关。


      oppo手机允许通知后仍需进一步操作

      再改改:

      Build.VERSION_CODES.O           =>  26
      Build.VERSION_CODES.LOLLIPOP    =>  21
      Build.VERSION_CODES.GINGERBREAD =>  9
      NotificationChannel channelUsed = manager.getNotificationChannel(notification.getChannelId());
      boolean isNotifyEnable = NotificationManagerCompat.from(this).areNotificationsEnabled();
      boolean isChannelEnable = true;
      if (Build.VERSION.SDK_INT >= 26) {
          isChannelEnable = channelUsed.getImportance() != NotificationManager.IMPORTANCE_NONE;
      }
      if (isNotifyEnable && isChannelEnable) {
          manager.notify(notifyId, notification);
      } else if (!isNotifyEnable) {
          if (Build.VERSION.SDK_INT >= 26) {
              startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
                        .putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()));
          }else if (Build.VERSION.SDK_INT >= 21) {
              startActivity(new Intent("android.settings.APP_NOTIFICATION_SETTINGS")
                        .putExtra("android.provider.extra.APP_PACKAGE", getPackageName()));
          } else if (Build.VERSION.SDK_INT >= 9) {
              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);
          } else {
              Intent intent = new Intent(Intent.ACTION_VIEW);
              intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
              intent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
              intent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());
              startActivity(intent);
          }
      }else{
         用户打开了通知开关,但是没有允许当前channel的通知,根据通知重要性自行决定如何提示用户
      }
      
      • 注意点1:查看安卓5.0(api21)与8.0(api26)的源码可知Settings. ACTION_APP_NOTIFICATION_SETTINGS 的值其实就是 "android.settings. APP_NOTIFICATION_SETTINGS",只是在安卓8.0以下这个常量对开发者是隐藏的。因此上述代码如想简化可省略if(Build.VERSION.SDK_INT >= 26){...}这一块。
      • 注意点2:NotificationChannel channelUsed 在构建通知的时候使用的NotificationChannel和真实使用的NotificationChannel可能是不同的。因为createNotificationChannels在service层实现的时候保存了一个record缓存,notify的时候是从record取的channel对象,此时importance的值极可能已经产生了变化。因此需要使用manager.getNotificationChannel(notification.getChannelId())返回的NotificationChannel对象
      • 小插曲:测试时发现vivo X21有两个通知管理页面:
        明显右边很漂酿啊有木有 vivo的设计很符合我的审美哈哈,试试改下跳转,左边的太丑了不要了。通过shell 命令dumpsys activity | grep -i run找到落地页:com.android.systemui / com.vivo.systemui.statusbar.notification.settings.channels.NotificationSettingsActivity 先不管参数跑下试试,不出所料崩了,提示Permission Denial: starting Intent{...}not exported from uid 10023。activity没有设置exported属性为true,无法访问。暂时想不到怎么暴力破解...
  • 3:targetSdkVersion<26时的机型适配

    • 3.1: 即便你的compileSdkVersion小于26,在oppo/vivo的高版本系统上,通知仍然默认是关闭的。也就是说:在目前安卓7.0/8.0占据主流的情况下通知栏适配是 必须 要做的适配。
      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省略
      

相关文章

网友评论

      本文标题:notification的适配

      本文链接:https://www.haomeiwen.com/subject/yvpfyqtx.html