极客星球 | MobPush之FCM离线消息解密

极客星球 | MobPush之FCM离线消息解密
文章图片

一、背景 MobPush已实现tcp方式实时推送,但前提是需要APP处于运行模式,离线模式下无法收到推送,为了解决此问题,提高推送到达率,MobPush接入华为、小米、oppo、vivo、FCM、魅族六大厂商推送。现因厂商推送到达时,应用无法获取到通知信息,所以无法确认厂商通知是否真正达到客户端。
经对FCM分析,找到相关方案,无论在线还是离线状态下接收FCM厂商消息都能获取到具体内容,本文将与大家分享FCM推送消息获取小妙招。
二、探索发现 本次探索,直接通过FCM后台进行推送,并使用Google pixel手机和oppo海外手机分别进行实验。当使用Google pixel手机时,发现无论应用是否处于运行状态均能收到FCM消息,且在离线状态下收到消息之后,应用进程被唤醒。当使用oppo海外版测试推送时,在线状态正常接收消息,但在离线状态下无法收到消息,通过观察APP运行日志发现,每次收不到消息时FCM都会有日志信息:
极客星球 | MobPush之FCM离线消息解密
文章图片

【极客星球 | MobPush之FCM离线消息解密】经过对FCM sdk和google play service进行反编译,终于在google play service的BroadcastDoneReceiver类中找到日志出处:
极客星球 | MobPush之FCM离线消息解密
文章图片

通过搜索BroadcastDoneReceiver的创建和调用栈,最终追溯到TCP读取消息的地方,具体逻辑是:搜到tcp消息解析之后,创建BroadcastDoneReceiver实例,创建action为““com.google.android.c2dm.intent.RECEIVE””的intent,发送有序广播:
极客星球 | MobPush之FCM离线消息解密
文章图片

根据该接口源码的介绍,以BroadcastDoneReceiver实例为参数,系统会以该实例作为广播的最后接收者,用来获取广播的发送结果,当有应用获取收到广播时会调用setResult方法设置结果,并在BroadcastDoneReceiver实例中通过getResult获取结果,当无应用收到广播时,便有以上所说错误日志诞生。
进一步探索,发现应用集成FCM后,会在AndroidManifest.xml中注册上面说到的action对应的广播接收者FirebaseInstanceIdReceiver,其信息如下图:
极客星球 | MobPush之FCM离线消息解密
文章图片

查看FirebaseInstanceIdReceiver的源码,发现确有通知消息的处理逻辑,以及setResult的调用,如下图,证实了上面的结论:
极客星球 | MobPush之FCM离线消息解密
文章图片

于是我们在应用中自定义广播,并按上图方式注册在AndroidManifest.xml中,然后推送FCM消息,惊喜发现能正常接收到广播,并且在广播的intent中已带有通知所有信息。
三、总结 FCM厂商消息到达时,无论在线还是离线状态下,都会发送广播FirebaseInstanceIdReceiver,并且带有推送消息内容,所以我们可以用相同方式在应用中注册自定义的广播获取推送消息,进行统计或者有需要的业务需求。之后也会对其他厂商进行调研,看是否有类似发现。

    推荐阅读