海外 APP 推送(上篇):厂商通道与谷歌 FCM 通道的差异
作者:极光高级工程师——史坤坤
企业出海图景
在疫情持续,叠加复杂多变的国际贸易环境下,中国对外直接投资流量和存量连续四年稳居全球前三,近八成中国企业将维持和扩大对外投资意向,看好对外投资前景。
企业出海,第一要务就是要建立与用户的触达通道。在 APP 环境之外,海外触达用户的通道与国内用户略有区别,海外是以邮件为主要的通道,而国内主要是以短信为主,辅以微信等社交通道。在 APP 环境之内,海外与国内触达用户的区别不大,均是以 APP 推送为主。
接下来,我们着重从海外 APP 推送通道来做一些对比与分析。
APP 消息推送通道现状
在海外,手机厂商除苹果外,安卓仍然以三星为领头羊,独占 19%的市场份额。小米、OPPO、Vivo、realme 紧随其后,市场份额占比分别为 12%、9%、8%以及 6%。
图一:2021 年第四季度全球智能手机出货量报告(数据来自市调机构 Conuterpoint Research)
企业 APP 的消息触达用户的通道,最重要的仍然还是推送通知消息。各个头部厂商对 Android 原生系统均有所定制,但与国内环境还是有所区别。
在中国大陆,谷歌受地域限制,无法使用谷歌相关服务,国内厂商对 GMS 服务套件进行了系统层的阉割,谷歌官方的 FCM 推送通道也相应的无法使用。进而替代的是各个厂商自己的厂商通道。顾名思义,厂商通道就是指手机硬件厂商提供的系统级别的推送通道,因为是系统服务,随着设备开机后就一直存在着,有效的保证了推送通道长连接的高可用性。
在海外,由于安卓系统默认支持谷歌 FCM 通道,且网络环境不受地域限制,因此厂商未对出口海外版的手机进行 GMS 的阉割,而是保留厂商通道与谷歌 FCM 通道共存的方式。谷歌 FCM 通道,是安卓系统自带的通道服务,与谷歌 Firebase 后台保持长连接,服务归属是谷歌而非设备厂商。
经实际测试、同时与厂商官方技术沟通后,总结关于通知通道的使用情况如下:
厂商通道与谷歌 FCM 通道的区别
那么,既然海外的 Google 服务是不受限制的,为什么厂商还要保留两个通道呢?他们有什么具体的区别吗?确实是存在一些区别。
区别一:就是上边提到的通道归属服务方不同,厂商通道由硬件设备厂商提供服务,谷歌 FCM 通道由谷歌官方提供服务。
区别二:厂商通道,在网络通畅且推送消息内容合法的情况下,通过厂商通道推送消息给该厂商设备,不论应用进程是否存活,都能保证消息可以推送到位。所以,厂商通道是消息高效触达的一种保障。同时,也对企业用户的拉活、促新、留存有一定的提升。而 FCM 通道,通过 Firebase 后台推送消息给安卓设备时,设备收到消息后,会先根据应用的某种状态来决定是否展示消息。如果 APP 进程被用户主动杀死,将不会继续进行展示推送消息。
对于这个规则,起初我们也是持有怀疑态度,如果这样的话,那与 APP 自己实现消息推送相比,就没有高可用的通道服务的优势了,难道只是为了帮助开发者简化推送服务的实现流程,降低开发成本?带着这个疑问,我们展开了专项测试和分析。分析过程如下:
验证进程存活与被杀死情况下,消息的展示情况的现象。
分析消息的生命周期状态
深入分析系统源码、GMS 源码、FCM 源码
总结结论
下面是具体的分析过程,涉及到系统源码的分析,如果不感兴趣可略过阅读,直接看最后总结的结论。
1.验证进程存活与被杀死情况下,消息的展示情况的现象
a. 退入后台,无操作停留 2 小时
通过 usb 连接手机,进入 ADB 模式,执行 ps 命令后,进程仍然存在,说明并未被系统回收资源。此时发送推送消息,如预期,通知消息正常展示。
b. 退入后台,通过最近任务,滑动杀死 APP
同样,进入 ADB 后,通过 ps 命令查看进程状态,进程已不存在,但不确定资源是否被系统及时回收。此时发送推送消息,通知消息能正常展示。还比较符合厂商通道的优势特征。
c. 进入应用详情,强行停止 APP
不出意料的,进程肯定已经不存在了,ps 命令查看也是如此。同样的,不能确定资源是否被系统及时回收。此时发送推送消息,通知消息却不能正常展示。由此推测,FCM 通道不依赖 APP 的进程是否存活。这个特点,是优于 APP 自己实现推送通知的。
d. 重启手机
重启手机后,由于应用已没有接收重启系统的广播权限,进程肯定已经不存在了,同时,资源肯定也是被系统回收了的。此时发送推送消息,通知依然能正常展示。由此进一步确认,系统缓存了应用的某种状态,FCM 会依据该状态来决定是否展示通知。
2.分析消息的生命周期状态
有了以上试验的现象,那么接下来重点分析下为什么强行停止 APP 后,就不能正常收到通知并展示了。
首先看推送 API 的响应,正常。推送任务已提交至谷歌 Firebase 后台,姑且认为服务器已下发到设备。
通过 adb logcat >log.log 抓取现场系统日志,在 log 中有以下打印:
16600 16600 W GCM : broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE pkg=cn.jiguang.junion.jpushtestdemo (has extras) }
确认消息已正常送达到了设备,因此怀疑 GMS 的消息广播就没有正常发出来。
深入分析系统源码、GMS 源码、FCM 反编译代码
先根据 FCM 方法调用链,从消息分发处入手往上跟进,确认其调用步骤。
a. FirebaseMessagingService
在系统源码中跟踪到 FirebaseMessagingService 负责分发通知消息到 SDK。
b. EnhancedIntentService 负责解析处理收到的消息
c. 继续分析消息是哪里接收来的,跟踪到是通过 AIDL 形式把广播中的消息传到 Service 中的
然后发送到 FirebaseInstanceIdReceiver
d. 最终的广播来源 CloudMessagingReceiver
因此可以明确知道:FCM 的消息也是从广播来的。
4.总结结论
最终,在 AMS 中找到了答案。应用被用户主动 kill 后,系统直接把死亡进程所属的广播,都直接过滤掉了,从而不对其发送广播。
在 Android 系统中,应用被用户主动 kill 后,在 ams 中会调用 finishForceStopPackageLocked()中 发送内部广播:ACTION_PACKAGE_RESTARTED, 它会限制包的自启或者通知移除等等。而广播是通过 sendBroadcast 来发送的,在 AMS broadcastIntentLocked 中,明确添加了 intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);该 Flag 后续会从系统中查找缓存,过滤广播的代码:
registeredReceivers = mReceiverResolver.queryIntent(intent,resolvedType, false /defaultOnly/, userId);
在 IntentResolver.queryIntent 方法内部有调用:
final boolean excludingStopped = intent.isExcludingStopped();
if (excludingStopped && isFilterStopped(filter, userId)) { -------这里会过滤Stopped 和进程Stop的
if (debug) {
Slog.v(TAG, " Filter's target is stopped; skipping");
}
}
isExcludingStopped()的定义代码:
public boolean isExcludingStopped() {
return (mFlags&(FLAG_EXCLUDE_STOPPED_PACKAGES|FLAG_INCLUDE_STOPPED_PACKAGES))== FLAG_EXCLUDE_STOPPED_PACKAGES;
}
官方的 issues 也有类似的官方回复,感兴趣的可以看下:
引自:https://github.com/firebase/firebase-android-sdk/issues/2917
更多关于海外厂商通道的集成方法,请看下回分解。
关于极光
极光(Aurora Mobile,纳斯达克股票代码:JG)成立于 2011 年,是中国领先的客户互动和营销科技服务商。成立之初,极光专注于为企业提供稳定高效的消息推送服务,凭借先发优势,已经成长为市场份额遥遥领先的移动消息推送服务商。随着企业对客户触达和营销增长需求的不断加强,极光前瞻性地推出了消息云和营销云等解决方案,帮助企业实现多渠道的客户触达和互动需求,以及人工智能和大数据驱动的营销科技应用,助力企业数字化转型。
版权声明: 本文为 InfoQ 作者【极光JIGUANG】的原创文章。
原文链接:【http://xie.infoq.cn/article/637988006a8b0b0015072c376】。文章转载请联系作者。
评论