版权声明:本文为xing_star原创文章,转载请注明出处!

本文同步自http://javaexception.com/archives/178

对Android 8.0以上版本通知点击无效的一次分析

最近在重构聊天服务,有机会从新梳理下前人写的通知相关的逻辑,隐藏着一个很深的bug,遗留了应该有8个多月了。直到今天才定位出原因,之前一直怀疑是自己设备的问题😂😂,经过写demo,做实验验证,最终得出结论。
Android 8.0之后通知相关的Api又发生了很大的变化,接手项目的时候,没怎么关注过这块,一直以为没有问题,到今天测试验证,发现还是存在问题的😂,问题表象是,点击App通知栏的通知消息,点击之后通知栏还在,一直没有反应(App业务中关于Click通知栏的逻辑没有生效)。抱着对代码怀疑的角度,自己创建了个demo工程,参考Notification的用法,自己做实验观察效果。

实验过程

这里在回顾下自己做实验的过程。

首先创建了个demo工程,添加通知相关的代码,以及动态注册了一个BroadcastReceiver。

private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
 
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent == null || context == null) {
            return;
        }
        mNotificationManager.cancel(NOTIFICATION_ID_LIVE);
        String type = intent.getStringExtra(PUSH_TYPE);
        if (PUSH_TYPE_LINK.equals(type)) {
            mNumLinkes = 0;
        } else if (PUSH_TYPE_LIVE.equals(type)) {
            mNumLives = 0;
        }
        //这里可以重新计数
    }
};
private void registerHeadsetPlugReceiver() {
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(NOTIFICATION_CLICK_ACTION);
    intentFilter.addAction(NOTIFICATION_DELETED_ACTION);
    registerReceiver(mBroadcastReceiver, intentFilter);
}
private void sendLiveNotification() {
    Intent intent = new Intent(NOTIFICATION_CLICK_ACTION);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
        channel.setBypassDnd(true);    //设置绕过免打扰模式
        channel.canBypassDnd();       //检测是否绕过免打扰模式
        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);//设置在锁屏界面上显示这条通知
        channel.setDescription("测试通知消息内容");
        channel.setLightColor(Color.GREEN);
        channel.setName("测试通知消息名称");
        channel.setShowBadge(true);
        channel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
        channel.enableVibration(true);
        mNotificationManager.createNotificationChannel(channel);
    }
 
    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID);
    String title = "Push测试";
    mBuilder.setContentTitle(title);
    mBuilder.setTicker(title);
    mBuilder.setContentText("https://233.tv/over140");
    mBuilder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
    mBuilder.setSmallIcon(R.mipmap.ic_launcher);
    mBuilder.setDefaults(Notification.DEFAULT_ALL);
    mBuilder.setWhen(System.currentTimeMillis());
    mBuilder.setContentIntent(PendingIntent.getBroadcast(this, NOTIFICATION_ID_LIVE, intent, 0));
    mBuilder.setDeleteIntent(PendingIntent.getBroadcast(this, NOTIFICATION_ID_LIVE, new Intent(NOTIFICATION_DELETED_ACTION).putExtra(PUSH_TYPE, PUSH_TYPE_LIVE), 0));
    mNotificationManager.notify(NOTIFICATION_ID_LIVE, mBuilder.build());
}

第一步的测试代码比较简单,在MainActivity调用sendLiveNotification()方法,就会出现通知栏,之后呢,点击通知栏会发送广播,我们在MainActivity注册了这个广播事件,就能够接收到,看起来没问题。到这里把怀疑点放到了静态注册上,BroadcastReceiver在AndroidManifest.xml中的注册。

接着就是修改代码,把动态注册的代码调整为一个CustomBroadcastReceiver,然后在AndroidManifest.xml中进行注册

<receiver
    android:name=".CustomBroadcastReceiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter>
        <action android:name="me.star.notificationdemo2.click" />
        <action android:name="me.star.notificationdemo2.delete" />
    </intent-filter>
</receiver>
public class CustomBroadcastReceiver extends BroadcastReceiver {
    private int NOTIFICATION_ID_LIVE = 101;
    private String PUSH_TYPE_LIVE = "PUSH_TYPE_LIVE";
    private String PUSH_TYPE = "push_type";
    private String PUSH_TYPE_LINK = "PUSH_TYPE_LINK";
    private int mNumLinkes;
    private int mNumLives;
 
    @Override
    public void onReceive(Context context, Intent intent) {
        NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        if (intent == null || context == null) {
            return;
        }
        mNotificationManager.cancel(NOTIFICATION_ID_LIVE);
        String type = intent.getStringExtra(PUSH_TYPE);
        if (PUSH_TYPE_LINK.equals(type)) {
            mNumLinkes = 0;
        } else if (PUSH_TYPE_LIVE.equals(type)) {
            mNumLives = 0;
        }
    }
}

调整完后,继续验证自己的想法。

运行程序,开启debug模式,发现点击通知栏后,在CustomBroadcastReceiver中没有拦截到,到这里就确定了问题肯定是出现在静态注册上面,但是之前的项目中,有些第三方的broadcastReceiver是静态注册,但最终是执行了onReceiver的方法,那肯定是我的使用姿势有问题。于是google搜索了一番,关键词就是android broadcastreceiver androidmanifest android 8.0 不运行,第一篇文章就是我想要找的。参考了一番,发现提到的几个说法值得一试,目前只验证了

intent.setPackage(getPackageName());

在查阅资料的过程也特意观察了下log输出

07-26 23:30:14.452 1637-1688/? W/BroadcastQueue: Background execution not allowed: receiving Intent { act=me.star.notificationdemo2.click flg=0x10 } to notification.star.me.notificationdemo2/.CustomBroadcastReceiver

说的是后台执行不被允许?反正搞不懂啥情况,没仔细看相关的源码。加上intent.setPackage(getPackageName());后重新编译

之后重新debug,发现这次执行了onReceiver的代码逻辑,终于找到了问题所在,就这一行代码,花去了好几个小时😂,但是值得的。

 

参考资料

https://blog.csdn.net/u011386173/article/details/82889275

Demo下载

链接:https://pan.baidu.com/s/1Ac97_0kFxKavpQi6-YxB2w 密码:niun  (demo中包含Git版本控制,可以通过切换commit,查阅动态注册,静态注册的代码)