Kotlin推送通知Firebase Cloud Messaging
2024-02-144.9k 阅读
简介
在移动应用开发中,推送通知是一种强大的工具,它可以帮助开发者与用户保持互动,向用户及时传达重要信息。Firebase Cloud Messaging (FCM) 是一个跨平台的消息传递解决方案,可让您免费可靠地向 Android、iOS 和 Web 应用发送消息。Kotlin 作为 Android 开发的首选语言,与 FCM 集成可以轻松实现推送通知功能。本文将详细介绍如何在 Kotlin 项目中集成 FCM 实现推送通知。
准备工作
- 创建 Firebase 项目:
- 首先,访问 Firebase 控制台(https://console.firebase.google.com/ )。
- 点击“添加项目”,按照提示输入项目名称并完成创建。
- 将 Firebase 添加到 Android 项目:
- 在 Android Studio 项目中,打开项目级别的
build.gradle
文件,确保 Google 的 Maven 仓库和 Gradle 插件依赖配置正确。例如:
- 在 Android Studio 项目中,打开项目级别的
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath 'com.google.gms:google-services:4.3.15'
}
}
- 在应用级别的 `build.gradle` 文件中,添加 Firebase Messaging 依赖。例如:
dependencies {
implementation 'com.google.firebase:firebase - messaging - ktx:23.0.6'
}
apply plugin: 'com.google.gms.google - services'
- 配置 Firebase 云消息传递:
- 在 Firebase 控制台中,进入项目设置,下载
google-services.json
文件,并将其复制到 Android 项目的app
目录下。 - 在 AndroidManifest.xml 文件中,添加以下权限:
- 在 Firebase 控制台中,进入项目设置,下载
<uses - permission android:name="android.permission.INTERNET"/>
<uses - permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
- 同时,注册 `FirebaseMessagingService` 和 `FirebaseInstanceIdService`(在 Kotlin 中它们是扩展的服务类)。例如:
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent - filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent - filter>
</service>
<service
android:name=".MyFirebaseInstanceIdService"
android:exported="false">
<intent - filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent - filter>
</service>
实现 Firebase Cloud Messaging 服务
- 创建 FirebaseMessagingService 子类:
- 继承
FirebaseMessagingService
类,该类负责处理接收到的推送消息。
- 继承
import android.util.Log
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class MyFirebaseMessagingService : FirebaseMessagingService() {
private val TAG = "MyFirebaseMsgService"
override fun onMessageReceived(remoteMessage: RemoteMessage) {
// 处理接收到的消息
Log.d(TAG, "From: ${remoteMessage.from}")
// 检查消息是否包含通知内容
remoteMessage.notification?.let {
Log.d(TAG, "Message Notification Body: ${it.body}")
sendNotification(it.body.toString())
}
// 处理自定义数据
remoteMessage.data.isNotEmpty().let {
Log.d(TAG, "Message data payload: ${remoteMessage.data}")
}
}
private fun sendNotification(messageBody: String) {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(this, getString(R.string.default_notification_channel_id))
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(getString(R.string.app_name))
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// 创建通知渠道(Android 8.0 及以上需要)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(getString(R.string.default_notification_channel_id),
"FCM Notifications",
NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build())
}
}
- 创建 FirebaseInstanceIdService 子类(可选,但推荐):
- 继承
FirebaseInstanceIdService
类,用于获取设备的 FCM 令牌。FCM 令牌是设备在 FCM 服务中的唯一标识符,在向特定设备发送推送通知时会用到。
- 继承
import android.util.Log
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.iid.FirebaseInstanceIdService
class MyFirebaseInstanceIdService : FirebaseInstanceIdService() {
private val TAG = "MyFirebaseIIDService"
override fun onTokenRefresh() {
// 获取最新的 FCM 令牌
val refreshedToken = FirebaseInstanceId.getInstance().token
Log.d(TAG, "Refreshed token: $refreshedToken")
// 将令牌发送到您的服务器(如果需要)
sendRegistrationToServer(refreshedToken)
}
private fun sendRegistrationToServer(token: String?) {
// 这里实现将令牌发送到您的服务器逻辑
}
}
处理不同类型的推送消息
- 通知消息:
- 通知消息由 FCM 直接处理并显示给用户,不需要应用在前台运行。在
MyFirebaseMessagingService
的onMessageReceived
方法中,remoteMessage.notification
包含了通知的标题、内容等信息。例如:
- 通知消息由 FCM 直接处理并显示给用户,不需要应用在前台运行。在
remoteMessage.notification?.let {
Log.d(TAG, "Message Notification Body: ${it.body}")
sendNotification(it.body.toString())
}
- 数据消息:
- 数据消息需要应用在前台运行时自行处理。在
MyFirebaseMessagingService
的onMessageReceived
方法中,remoteMessage.data
包含了自定义的数据。例如:
- 数据消息需要应用在前台运行时自行处理。在
remoteMessage.data.isNotEmpty().let {
Log.d(TAG, "Message data payload: ${remoteMessage.data}")
// 在这里根据数据执行相应的逻辑
}
- 混合消息:
- 混合消息既包含通知部分又包含数据部分。同样在
onMessageReceived
方法中,可以分别处理通知和数据部分。例如:
- 混合消息既包含通知部分又包含数据部分。同样在
remoteMessage.notification?.let {
Log.d(TAG, "Message Notification Body: ${it.body}")
sendNotification(it.body.toString())
}
remoteMessage.data.isNotEmpty().let {
Log.d(TAG, "Message data payload: ${remoteMessage.data}")
// 处理数据
}
发送推送通知
- 使用 Firebase 控制台发送测试通知:
- 在 Firebase 控制台中,进入“云消息传递”选项卡。
- 选择“发送测试消息”,可以选择目标设备(通过 FCM 令牌或主题),输入通知标题和内容,然后点击“发送”。
- 从服务器发送推送通知:
- 如果您有自己的服务器,可以使用 FCM 的 HTTP v1 API 来发送推送通知。以下是一个简单的使用 cURL 发送通知的示例:
curl -X POST -H "Authorization: Bearer <server - key>" -H "Content - Type: application/json" -d '{
"message": {
"token": "<device - token>",
"notification": {
"title": "Test Notification",
"body": "This is a test push notification"
}
}
}' "https://fcm.googleapis.com/v1/projects/<project - id>/messages:send"
- 其中 `<server - key>` 可以在 Firebase 控制台的“项目设置” - “云消息传递”中找到,`<device - token>` 是目标设备的 FCM 令牌,`<project - id>` 是您的 Firebase 项目 ID。
主题消息推送
- 订阅主题:
- 应用可以让用户订阅特定的主题,这样就可以向所有订阅该主题的设备发送推送通知。在 Kotlin 代码中,可以使用以下方式订阅主题:
FirebaseMessaging.getInstance().subscribeToTopic("news")
.addOnCompleteListener { task ->
var msg = "Subscribed"
if (!task.isSuccessful) {
msg = "Subscribe failed"
}
Log.d(TAG, msg)
}
- 发送主题消息:
- 从服务器发送主题消息时,将目标设置为主题名称。例如,使用 FCM HTTP v1 API:
curl -X POST -H "Authorization: Bearer <server - key>" -H "Content - Type: application/json" -d '{
"message": {
"topic": "news",
"notification": {
"title": "New News",
"body": "There is a new news article available"
}
}
}' "https://fcm.googleapis.com/v1/projects/<project - id>/messages:send"
高级功能和优化
- 消息优先级:
- FCM 支持设置消息优先级,高优先级的消息会优先发送并立即显示给用户,而低优先级的消息可能会在设备处于节能模式等情况下延迟发送。在发送消息时,可以通过设置
priority
字段来指定优先级。例如,在服务器端发送通知时:
- FCM 支持设置消息优先级,高优先级的消息会优先发送并立即显示给用户,而低优先级的消息可能会在设备处于节能模式等情况下延迟发送。在发送消息时,可以通过设置
{
"message": {
"token": "<device - token>",
"notification": {
"title": "High Priority Notification",
"body": "This is a high priority push notification"
},
"priority": "high"
}
}
- 消息有效负载大小限制:
- FCM 对推送消息的有效负载大小有限制。对于通知消息,总大小限制为 4096 字节,对于数据消息,限制为 4096 字节(如果使用可折叠标头,则为 2048 字节)。在构建消息时,需要确保不超过这些限制。
- 处理多语言通知:
- 如果您的应用面向多个语言区域的用户,可以在通知中提供多语言支持。可以通过在服务器端根据设备的语言设置发送不同语言的通知内容,或者在应用端根据设备语言动态处理通知内容。例如,在
MyFirebaseMessagingService
中,可以根据设备语言设置选择不同的通知字符串资源:
- 如果您的应用面向多个语言区域的用户,可以在通知中提供多语言支持。可以通过在服务器端根据设备的语言设置发送不同语言的通知内容,或者在应用端根据设备语言动态处理通知内容。例如,在
private fun sendNotification(messageBody: String) {
val context = applicationContext
val resources = context.resources
val appName = resources.getString(R.string.app_name)
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(this, getString(R.string.default_notification_channel_id))
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(appName)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// 创建通知渠道(Android 8.0 及以上需要)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(getString(R.string.default_notification_channel_id),
"FCM Notifications",
NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build())
}
在这个例子中,appName
是从字符串资源中获取的,根据设备语言设置会加载相应语言的字符串。
- 优化电池使用:
- 为了减少推送通知对设备电池的消耗,可以尽量避免频繁发送不必要的推送。对于低优先级的消息,可以考虑批量发送或者在设备处于充电状态等合适时机发送。同时,在处理推送消息时,尽量减少长时间运行的任务,避免过度占用系统资源。例如,在
MyFirebaseMessagingService
中,如果接收到数据消息需要进行一些数据处理,可以使用WorkManager
等工具在后台异步处理,并且设置合适的约束条件,如在设备充电、有网络连接等情况下执行任务。
- 为了减少推送通知对设备电池的消耗,可以尽量避免频繁发送不必要的推送。对于低优先级的消息,可以考虑批量发送或者在设备处于充电状态等合适时机发送。同时,在处理推送消息时,尽量减少长时间运行的任务,避免过度占用系统资源。例如,在
常见问题及解决方法
- 无法接收推送通知:
- 检查网络连接:确保设备已连接到网络,并且没有网络限制阻止 FCM 服务器与设备通信。
- 检查权限:确认应用已正确声明所需的权限,如
INTERNET
权限。 - 检查 Firebase 配置:检查
google - services.json
文件是否正确配置并放置在app
目录下,同时检查应用级和项目级的build.gradle
文件中 Firebase 依赖是否正确添加。 - 检查 FCM 令牌:确保设备获取到了正确的 FCM 令牌,可以在
MyFirebaseInstanceIdService
的onTokenRefresh
方法中打印令牌进行检查。如果令牌不正确,可能导致无法接收推送。
- 推送通知显示异常:
- 检查通知渠道设置:在 Android 8.0 及以上,通知渠道的设置非常重要。确保在
MyFirebaseMessagingService
中正确创建了通知渠道,并且设置了合适的重要性级别。 - 检查自定义布局:如果使用了自定义通知布局,确保布局文件正确无误,并且在
NotificationCompat.Builder
中正确设置了自定义布局。
- 检查通知渠道设置:在 Android 8.0 及以上,通知渠道的设置非常重要。确保在
- 服务器发送推送失败:
- 检查服务器密钥:确保在服务器端使用的 Firebase 服务器密钥正确,该密钥可以在 Firebase 控制台的“项目设置” - “云消息传递”中获取。
- 检查 API 调用:确认使用的 FCM HTTP v1 API 的调用格式正确,包括请求 URL、请求方法、请求头和请求体等。可以参考 FCM 官方文档进行检查。
通过以上步骤和内容,您可以在 Kotlin 项目中成功集成 Firebase Cloud Messaging 实现推送通知功能,并根据实际需求进行扩展和优化。在开发过程中,要密切关注官方文档的更新,以确保应用能够充分利用 FCM 的最新特性和功能。