当前位置: 首页 > article >正文

Springboot和Android项目接入Firebase Cloud Message(FCM)

一、官网

https://firebase.google.com/docs/cloud-messaging?hl=zh-cn

二、Springboot项目集成主要流程

  1. 创建 Firebase 项目并获取服务账号密钥
  2. 添加依赖项
  3. 配置 Firebase 服务
  4. 发送推送通知

1. 创建 Firebase 项目并获取服务账号密钥

  1. 登录 Firebase 控制台,创建一个新项目。
  2. 在左侧菜单中,点击 “Project settings”(项目设置)。
  3. 转到 “Service accounts”(服务账户)选项卡。
  4. 点击 “Generate new private key”,这将下载一个 JSON 文件,包含了你的 Firebase 服务账户的密钥。
  5. 将这个密钥文件保存到 Spring Boot 项目中(例如 src/main/resources/ 目录)。

2. 添加依赖项

implementation("com.google.firebase:firebase-admin:9.4.3")

3. 配置 Firebase 服务

初始化 Firebase App,加载服务账户密钥。

package com.cn.gasservice.config

import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions
import com.google.firebase.messaging.FirebaseMessaging
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.stereotype.Component
import java.io.FileInputStream

/**
 *    @Author : Cook
 *    @Date   : 2025/1/15
 *    @Desc   :
 *    @Version:
 */
@Configuration
class FirebaseConfig {
    init {
        val serviceAccount = FileInputStream("src/main/resources/fcm.json")

        val options = FirebaseOptions.builder()
            .setCredentials(com.google.auth.oauth2.GoogleCredentials.fromStream(serviceAccount))
            .build()

        if (FirebaseApp.getApps().isEmpty()) {
            FirebaseApp.initializeApp(options)
        }
    }


    @Bean("firebaseMessaging")
    fun getFirebaseMessaging(): FirebaseMessaging = FirebaseMessaging.getInstance()
}

4. 发送推送通知

使用firebaseMessaging.sendEachForMulticastAsync方法或者其他单独发送同步或者异步根据自己需求调用。主要参数【token】是由终端获取并上传而来,具体获取的方法在下面Android项目中。

推送内容可以自己调用putData方法传递,终端接手后自己根据消息类型自行处理,也可以使用对应的setNotification方法

 setNotification(Notification notification)

对于推送结果的记录如果是单独推送的话比较好处理,直接记录就可以,

如果是批量推送的话,可以根据token和用户ID的绑定关系来处理,参照下面的方法,或者有其他更好的办法可以交流一下。

package com.cn.gasservice.service.impl

import com.cn.gasservice.constant.consist.AppConstants.KEY_MESSAGE
import com.cn.gasservice.mapper.PushMessageLogMapper
import com.cn.gasservice.mapper.PushRegistrationMapper
import com.cn.gasservice.model.dto.PushMessageLogDTO
import com.cn.gasservice.model.dto.PushRegistrationDTO
import com.cn.gasservice.model.dto.SendMsgDTO
import com.cn.gasservice.model.vo.PushMsgVO
import com.cn.gasservice.service.intf.PushService
import com.cn.gasservice.utils.AppUtils.object2Json
import com.cn.gasservice.utils.LogUtils
import com.google.api.core.ApiFuture
import com.google.firebase.messaging.BatchResponse
import com.google.firebase.messaging.FirebaseMessaging
import com.google.firebase.messaging.MulticastMessage
import org.springframework.beans.BeanUtils
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Service
import java.util.concurrent.Executors

/**
 *    @Author : Cook
 *    @Date   : 2025/1/15
 *    @Desc   :
 *    @Version:
 */
@Service
class PushServiceImpl(
    @Qualifier("firebaseMessaging")
    private val firebaseMessaging: FirebaseMessaging,
    private val mapper: PushRegistrationMapper,
    private val pushMessageLogMapper: PushMessageLogMapper
) : PushService {
 

    override fun fcmMsg(msg: SendMsgDTO) {

        val map = mapper.queryTokenMapByUserId(msg.receivers)
        val tokens = map.keys

        val pushMsgVO = PushMsgVO()
        BeanUtils.copyProperties(msg, pushMsgVO)

        pushMsgVO.messageId = msg.id

        val pushData = object2Json(pushMsgVO)
        val multicastMessage = MulticastMessage.builder()
            .putData(KEY_MESSAGE, pushData)
            .addAllTokens(tokens)
            .build()

        val future = firebaseMessaging.sendEachForMulticastAsync(multicastMessage)


        updatePushLog(future, pushMsgVO, pushData, map, tokens)
        LogUtils.info("FCM push msg: $pushData")

    }


    fun updatePushLog(
        future: ApiFuture<BatchResponse>,
        msg: PushMsgVO,
        pushData: String,
        map: Map<String, PushRegistrationDTO>,
        tokenList: Collection<String>
    ) {
        future.addListener(Runnable {
            val tokens = arrayListOf<String>()
            tokens.addAll(tokenList)
            val result = future.get()  // 获取任务结果

            val pushLogList = arrayListOf<PushMessageLogDTO>()

            val sendResponseList = result?.responses ?: arrayListOf()
            sendResponseList.forEachIndexed { index, data ->
                LogUtils.info("updatePushLog: ${data.messageId} ---${data.isSuccessful}----${data.exception}")
                val token = tokens[index]
                pushLogList.add(PushMessageLogDTO().apply {
                    messageId = "${msg.messageId}"
                    userId = map[token]?.userId ?: ""
                    deviceType = map[token]?.deviceType ?: ""
                    pushStatus = if (data.isSuccessful) 1 else 2
                    errorMessage = data.exception?.toString()
                    errorCode = data.exception?.errorCode?.name
                    messageContent = pushData
                    registrationId = token
                })
            }
            val insertResult = pushMessageLogMapper.insertAll(pushLogList)
            LogUtils.info("updatePushLog success: $insertResult")

        }, Executors.newSingleThreadExecutor())

    }


    override fun registerPush(pushRegistration: PushRegistrationDTO): Boolean {

        val pg = mapper.selectByUserID(pushRegistration)
        return if (pg == null) {
            mapper.add(pushRegistration) > 0
        } else {
            pg.registrationId = pushRegistration.registrationId
            mapper.update(pg) > 0
        }

    }


}

三、Android项目集成流程

创建应用后根据官方流程添加集成即可

1、Android 13以上需要动态获取通知权限

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun MultiplePermissionsRequest() {
    val permissionsState = rememberMultiplePermissionsState(
        permissions = listOf(
            android.Manifest.permission.POST_NOTIFICATIONS,
        )
    )

    // 请求权限
    LaunchedEffect(Unit) {
        permissionsState.launchMultiplePermissionRequest()
    }

}

2、在MyApplication中的onCreate方法中初始化FCM

有可能初始化失败,加个Catch

    try {
            FirebaseApp.initializeApp(this)
        } catch (e: Exception) {
            e.printStackTrace()
        }

3、添加对应的服务

[onMessageReceived]方法处理收到的消息逻辑

[onNewToken]初始化成功后会收到token信息,可以判断当前是否登录,如果已登录就上传token和用户ID绑定,如果没有登录就先存储,等登录的时候一起上传到服务端。

package com.co.post.data.push

import android.util.Log
import com.co.post.data.model.db.Message
import com.co.post.data.repository.PushDataRepository
import com.co.post.utils.CacheHelper
import com.co.post.utils.Constants.KEY_PUSH_DATA
import com.co.post.utils.Constants.MSG_TYPE_TEXT
import com.co.post.utils.NotificationHelper
import com.co.post.utils.fromJson
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject

/**
 *    @Author : Cook
 *    @Date   : 2025/1/15
 *    @Desc   :
 *    @Version:
 */
class FCMService : FirebaseMessagingService() {
    private val pushDataRepository: PushDataRepository by inject()
    override fun onCreate() {
        super.onCreate()
    }
    override fun onMessageReceived(remoteMessage: RemoteMessage) {

        // 处理接收到的消息
        Log.e("FCM", "From: ${remoteMessage.from} ${remoteMessage.data[KEY_PUSH_DATA]} ")

        CoroutineScope(Dispatchers.Default).launch {
            try {
                val msgData = remoteMessage.data[KEY_PUSH_DATA] ?: ""
                if (msgData.isNotEmpty()) {
                    val msg = msgData.fromJson<Message>()
                    msg?.let {
                        it.isRead = false
                        pushDataRepository.add(it)
                        with(Dispatchers.Main) {
                            if (msg.messageType == MSG_TYPE_TEXT) {
                                NotificationHelper.showNotification(
                                    it.title,
                                    it.content,
                                    it.messageId
                                )
                            }
                        }
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }

        }
    }

    override fun onNewToken(token: String) {
        CacheHelper.pushToken = token
        if (CacheHelper.isLogged()) {
            CoroutineScope(Dispatchers.IO).launch {
                pushDataRepository.registerPush(token)
            }

        }

        // 获取新的 FCM 令牌
        Log.e("FCM", "Refreshed token: $token")
    }
}


http://www.kler.cn/a/512582.html

相关文章:

  • 用于牙科的多任务视频增强
  • 【深度学习项目】语义分割-FCN网络(原理、网络架构、基于Pytorch实现FCN网络)
  • 麒麟操作系统服务架构保姆级教程(十三)tomcat环境安装以及LNMT架构
  • LeetCode:37. 解数独
  • idea中远程调试中配置的参数说明
  • 【日志篇】(7.6) ❀ 01. 在macOS下刷新FortiAnalyzer固件 ❀ FortiAnalyzer 日志分析
  • 抛弃node和vscode,如何用记事本开发出一个完整的vue前端项目
  • 【Flutter】platform_view之AppKitView在哪个flutter版本添加的
  • 使用 Python 获取 1688 商品快递费用 API 接口的示例代码解析
  • [苍穹外卖] 1-项目介绍及环境搭建
  • 提升开发效率:Bash 脚本自动化环境搭建与依赖安装
  • 【Java面试】RabbitMQ
  • JSON解析时如何处理异常?
  • SpringBoot 接入 豆包 火山方舟大模型
  • Debian 上安装PHP
  • 【深度解析Java 20天速成】04_IDEA的安装与使用
  • ChromeOS 132 版本更新
  • 一文夯实垃圾收集的理论基础
  • 完整地实现了推荐系统的构建、实验和评估过程,为不同推荐算法在同一数据集上的性能比较提供了可重复实验的框架
  • docker pull error with proxy
  • 【Linux】常见指令(三)
  • YOLOv8改进,YOLOv8检测头融合DiverseBranchBlock,并添加小目标检测层(四头检测),适合目标检测、分割等
  • 手机怎么远程操控电脑?
  • 算法-求字符串公共前缀
  • Docker 部署 mysql
  • Java设计模式—观察者模式