浅谈Push推送服务
我相信每一个App都有Push功能,他是一个免费且对App营销非常重要的一个功能。无论是电商的促销活动,还是社交软件的通知,亦或是其他具有时效性的提醒都离不开Push通知。国内的Push推送百花齐放,但如果我们是在国外开展业务,则可以通过Google的Firebase框架中的Could Message(0成本且最稳定)解决这个问题。本文将主要介绍Firebase Could Message(FCM)的集成步骤及注意点。
Google FCM框架概览
所谓Push通知,简言之就是从一个地方将消息通过网络发送到移动客户端,再由移动客户端处理展示给用户的过程。这里一共有三个角色:消息的发送方、消息接收方和消息本身。但真要实现起来也没有那么容易,例如消息的格式、发送的时机、投递的顺序、触达率等问题都需要我们关心,处理。Google FCM框架的出现就是为了解决这些问题,这样我们就可以将更多的注意力放置于消息本身。整体上看,FCM框架的整体工作流程如下:我们按照事件流的处理从左往右看:
第一部分:消息的构建。消息的构建有两种方式:
- Firebase控制台(Notifications Console GUI)
- FCM Admin SDK:服务端接入 FCM Admin SDK后按照一定规则构建通知消息,此种更加灵活,方便我们根据不同用户画像做不同的App营销处理。本篇篇幅有限不做过多讨论。
第二部分:FCM BackEnd(FCM 后端),由Google负责,对已构建消息的分流处理,根据不同目标发送至不同平台用户。
第三部分:平台层,也就是我们Android 设备,这里更多指的是Android设备上的Google服务,完成最原始消息的接收。也就是说,只有有Google服务的手机才能收到FCM推送。
第四部分:FCM SDK,进一步对消息进行处理,确定分发策略,最终会发送(回调)给接入了FCM SDK的目标App。
当我们接入FCM时,只需关心第一、四部分即可。Google大大简化了我们的使用过程,在此感谢!
FCM 框架接入
这里我们选择使用Firebase FCM 控制台发送push通知(可以先忽视掉后端,我们调通后直接对接后端即可,无需做任何修改),我们集成FCM框架只需要关心两方面:
- Firebase控制台的设置
- 准备接入FCM SDK的Android项目
其实本质上就是之前提到的消息的发送方和接收方建立链接的过程,一旦链接建立后,便可以直接利用这个链接发送push通知。
Firebase控制台的设置
Firebase控制台的设置步骤如下:
使用Gmail登录至Firebase首页 Firebase 控制台 (google.com),点击顶部“➕”新建一个项目。
然后就会来到这个页面,这里需要设置项目名称,如果是公司已上线项目可以取App名称,这里我们演示用,直接取名FirebaseDemo,项目名称确定后,点击下一步。
第二步主要是确定是否开启Firebase的分析服务,默认开启,这里我们重点在fcm的集成上,所以先关闭。无论开启还是关闭,都不会影响到fcm功能。设置完成后直接点击“创建项目”,短时间的等待后,将会提示项目已就绪,这时候项目就创建好了。点击继续回到首页即可。
创建Android应用
一个Firebase项目是可以包含多个应用的,最简单来说,可以包含 android 和 iOS两个平台的同名称应用。来到Firebase项目首页,我们点击Android图标创建一个Android应用:
如果当前Firebase项目已经有其他应用,则可以通过继续点击“➕”或左侧齿轮进入项目设置添加新的应用。之后便会进入到如下界面:
包名就填我们App包名即可,签名证书选填可以先不管,填写完成后点击注册应用,Firebase会进行包名校验,校验通过后来到第二步:
这里会生成一个 google-services.json 的文件,其中包含了API 密钥、项目 ID、应用 ID等诸多信息,通过这些信息就可以唯一标识一个Android应用了。
按照指示将google-services.json文件添加到项目的制定目录下:
Android项目集成
对于FCM SDK的接入,对Android的项目有如下要求:
- Android最低版本大于等于Android4.4
- 安装有Google服务
- 使用 Jetpack (AndroidX),这需要满足以下版本要求:
com.android.tools.build:gradle 3.2.1 或更高版本
compileSdkVersion 28 或更高版本
添加依赖FCM SDK依赖
这里有两种方式:
- 通过Firebase Android BoM
- 单独引入FCM SDK
因为Firebase SDK集合除了FCM还有很多其他比较有用的SDK,如果需要同时引入这些SDK,难免会有一些依赖版本冲突的情况发生,所以更推荐使用Firebase Android BoM的方式引入,只需要指定目标BoM版本,后续只需按需引入其他Firebase SDK即可,这样可以保证所有引入的库的版本是兼容的。
通过Firebase BoM引入FCM
在模块(应用级)的build.gradle配置文件中引入依赖即可:
dependencies {
implementation(platform("com.google.firebase:firebase-bom:32.3.1"))
api 'com.google.firebase:firebase-messaging'
// ... 省略其他无关代码
// 如果后续需要引入 firebase-analytics ,只需添加如下代码即可,无需考虑 firebase-messaging 和 firebase-analytics 之间的版本兼容问题
// implementation 'com.google.firebase:firebase-analytics'
}
集成google-services插件
FCM SDK有两种初始化方式:
- 代码显示调用初始化
- 通过google-service自动初始化
这里我们选择引入google-services插件的方式,这样一方面在编译期gradle可以自动读取google-services.json中的内容,可以很大程度上减少了手动编码出错的可能。
另一方面,我们也无需在应用启动时,手动的编写初始化代码,让业务代码更加简洁。
初始化步骤如下:
1.在项目级别 build.gradle 文件中引入 google-services 插件:
buildscript {
repositories {
maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
maven { url "https://dl.google.com/dl/android/maven2" }
mavenCentral()
google()
}
dependencies {
// 引入 google-service插件
classpath 'com.google.gms:google-services:4.3.15'
// ...省略无关代码
}
}
allprojects {
repositories {
mavenCentral()
maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
maven { url "https://dl.google.com/dl/android/maven2" }
maven { url 'https://jitpack.io' }
google()
}
}
2.在应用级目录下的build.gradle文件中应用插件(应用级目录):
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.google.gms.google-services' // 应用 google-service插件
}
// ... 省略无关代码
至此,Firebase控制台和FCM SDK的引入就都完成了。
开始发送push消息
消息主要有以下3个注意点:
- 消息的分类
- 消息的接收(Android 设备/客户端)
- 消息的发送(Firebase控制台)
消息的分类
Fcm将Push消息按照用途分为了两种:
消息类型 | 处理方 | 发送方 |
---|---|---|
通知消息 | FCM SDK 自动处理 | Firebase控制台和接入FCM Admin SDK 的服务端 |
数据消息 | 客户端自行处理 | 接入了FCM Admin SDK的服务端 |
通知消息和数据消息都属于Fcm Push消息,那么Fcm是如何区分的呢?这就要从Push消息的格式来看。无论是通知消息还是数据消息都是以Json数据格式组织的,无非就是关键字段的Key不同罢了。
如果是通知消息,消息中将包含一个"notification"字段:
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"notification":{
"title":"标题",
"body":"我是内容!"
}
}
}
之前有提到,Push消息会经由Fcm SDK传递到我们的客户端,这里当Fcm SDK检测到消息中包含”notification“字段时,就会将其当做一个通知消息来处理。
而数据消息所谓的由客户端自行处理,也仍然经过Fcm SDK,只不过 Fcm SDK并不会对其进行任何处理,只是直接投递到客户端罢了。对应的,数据消息的识别字段是”data“。实例数据如下:
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"data":{
"Nick" : "Mario",
"body" : "内容",
}
}
}
如上所见,Fcm SDK 是通过识别关键字段来进行消息的分类处理,所以上述的”notification“,“data”都是fcm识别的关键字之一,定义数据消息时就需要特别注意,按需设置关键字段。同时如果要明确发送数据消息(非通知消息)还需要避开“from”、“message_type”或以“google”或“gcm”开头的任何字词。
无论是通知消息还是数据消息都包含了一个token字段,这个字段是由Fcm SDK生成用于标识当前客户端的,客户端可以将这个值保存下来并上报给服务端,这样后续服务端就可以通过指定token字段的值,定向向指定客户端发送消息了,做到精准营销用户。
不过token的值会如下情况下发生改变,例如:
- 在其他设备上重新登陆
- 用户卸载/重新安装应用
- 用户清除应用数据。
这里不做过多关心,这并不会影响到什么。
消息的处理
在初始设置阶段,我们完成了客户端FCM SDK的接入,如果想要顺利收到Push消息还需要完成如下设置:
1.扩展 FirebaseMessagingService 并在AndroidMainFest.XML中声明:
<service
android:name=".java.MyService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
2.token的保存和消息的接收
class MyService : FirebaseMessagingService() {
// 一旦token有变化,onNewToken方法就会被回调
override fun onNewToken(token: String) {
super.onNewToken(token)
Log.e(TAG, "new token $token")
}
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
Log.e(TAG,"onMessageReceived : ${message.messageType}")
}
override fun onDeletedMessages() {
super.onDeletedMessages()
Log.e(TAG,"onDeletedMessages ")
}
}
扩展 FirebaseMessagingService 后,有三个方法可以重写
- onNewToken:每次有新token生成时回调,我们可以利用这个机制保存token
- onMessageReceived:
- a:对于通知消息:如果App在前台并收到消息时,这个方法就会回调。RemoteMessage的数据结构和之前提到的Json一一对应,这就不展开了。若果App处于后台,消息则会直接展示在通知栏(需通知权限)
- b:对于数据消息:无论App处于前台还是后台,都会回调到这个方法
- c:参数RemoteMessage中的 Notification 类型的notification对象,用于承载通知消息的数据
- onDeletedMessages : Push消息被删除时回调
至此,我们的客户端就做好了接收消息的准备。但是此时App只能在设备解锁的前提下收到消息,如果想要在解锁前收到消息,还需要完成如下设置:
添加 firebase-messaging-directboot 依赖
/ 如果是Firebase BoM方式依赖
implementation 'com.google.firebase: firebase-messaging-directboot'
// 否则
implementation 'com.google.firebase:firebase-messaging-directboot:20.2.0'
给Service添加directBootAware属性
<service
android:name=".MyService"
android:exported="false"
android:directBootAware="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
通知权限
如果需要在通知栏展示Push消息,对于Android 13及以上设备需要显示申请通知权限
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
代码动态申请权限:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, "android.permission.POST_NOTIFICATIONS") ==
PackageManager.PERMISSION_GRANTED
) {
} else {
}
}
消息的发送
当客户端做好接收消息的准备后,就可以通过Firebase控制台创建一个Push消息测试一下了,在Firebase控制面板左侧Tab列表中找到Messaing选项
如果是第一次创建Push消息,则会来到如下界面,这里选择“创建首个宣传活动”(需要当前Firebase登录账户拥有当前项目的所有者或修改者权限)。
之后会弹起如下弹窗,选择“Firebase通知消息”。
选择“Firebase通知消息”之后,便可以进入Push消息的具体设置界面了,这里我们随便设置一个标题和通知内容。
再正式发布前,可以点击右侧的发送测试消息进行测试:
这里需要添加一个“FCM注册令牌”,还记得我们上文提到token吗,填写到这里就可以了(如果之前忘记保存了,可以通过清除缓存并重启App得到)。
添加完成后,在App测添加打印token:
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
Log.e(TAG, "Title :${message.notification?.title.orEmpty()}")
Log.e(TAG, "Body :${message.notification?.body}")
}
然后我们将App切换到前台,点击测试,onMessageReceived就会被回到如下:
将App切换到后台后,再次点击测试,可以看到通知栏有通知收到。
但是因为我们没做设置,所以通知展示了默认图标和颜色,这里可以自定义通知图标:
// AndroidmainFest.xml 中
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_stat_ic_notification" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorAccent" />
至此,整体流程完成,Yes!!!
Github地址(欢迎下载):https://github.com/loveAndroidAndroid/FirebaseDemo
出海共济
出海之路,路远且艰。更多金融出海解决方案,欢迎关注公众号 趣浪出海 ,欢迎大家一起探讨更多合规问题,稳健航行世界之海。