前言
最近在開發(fā)一款國際版的APP午乓,項目中需要支持客戶端消息推送益愈,自己實現(xiàn)肯定是不可能的,需要尋找第三方的SDK敏释。在做技術(shù)調(diào)研的時候決定使用google的FCM框架來實現(xiàn)钥顽,有個缺點就是大陸是接收不到的(fq可以)蜂大。那么本章就給大家分享一下如何基于Spring Boot集成Firebase實現(xiàn)FCM消息推送功能县爬。
必要條件
1、大陸開發(fā)者要準(zhǔn)備好vpn(你懂的)添谊。
2财喳、申請Google Firebase賬號。
3斩狱、獲取Firebase秘鑰耳高。
4、有效的客戶端token令牌(app客戶端開發(fā)者提供)所踊。
開發(fā)
第一步:申請開發(fā)者賬號
地址:https://console.firebase.google.com/
第二步:添加一個項目
如下圖所示泌枪,添加一個項目:
TPS:注意必須與android項目本地包名一致就是manifest中package的路徑一樣秕岛。
第三步:下載google-services.json文件
點擊左上角——設(shè)置(圖標(biāo))——項目設(shè)置碌燕,點擊google-services.json下載误证,如下圖所示:
將下載好的文件放到Spring Boot項目的resources目錄下,例如:
第四步:集成Firebase
pom引用firebase修壕,要fq才能下載愈捅,如果沒有條件就需要手動導(dǎo)入下載好的jar包。
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>6.5.0</version>
</dependency>
第五步:編寫FCM推送消息請求實體
這里只是做了個發(fā)送消息的簡單封裝實體慈鸠,方便數(shù)據(jù)傳輸:
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
*
* 功能描述: FCM推送消息請求實體
* @auther: IT實戰(zhàn)聯(lián)盟Line
* @date: 2019年11月25日09:54:51
*/
@NoArgsConstructor
@Data
@ApiModel(value = "FCM推送消息請求實體")
public class FCMSendMessageReqDTO implements Serializable {
private static final long serialVersionUID = 8317264020451674205L;
@ApiModelProperty(value = "消息標(biāo)題" , required = true)
private String title;
@ApiModelProperty(value = "消息內(nèi)容" , required = true)
private String body;
@ApiModelProperty(value = "用戶token集合" , required = true)
private String tokens;
@ApiModelProperty(value = "主題" , required = false)
private String topic;
}
第六步:編寫FCM推送消息工具類
該工具類支持單個和批量推送蓝谨,這里批量采用的是給主題推。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import com.google.auth.oauth2.GoogleCredentials;
import java.util.concurrent.ConcurrentHashMap;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.AndroidConfig;
import com.google.firebase.messaging.AndroidConfig.Builder;
import com.google.firebase.messaging.AndroidNotification;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;
import com.google.firebase.messaging.TopicManagementResponse;
/**
* @Auther: IT實戰(zhàn)聯(lián)盟Line
* @Date: 2019-11-23 14:02
* @Description: Google_FireBase推送工具類
*/
public class FireBaseUtil {
//存放多個實例的Map
private static Map<String, FirebaseApp> firebaseAppMap = new ConcurrentHashMap<>();
//獲取AndroidConfig.Builder對象
private static com.google.firebase.messaging.AndroidConfig.Builder androidConfigBuilder=AndroidConfig.builder();
//獲取AndroidNotification.Builder對象
private static AndroidNotification.Builder androidNotifiBuilder=AndroidNotification.builder();
/**
* 判斷SDK是否初始化
* @param appName
* @return
*/
public static boolean isInit(String appName) {
return firebaseAppMap.get(appName) != null;
}
/**
* 初始化SDK
* @param jsonPath JSON路徑
* @param dataUrl firebase數(shù)據(jù)庫
* @param appName APP名字
* @throws IOException
*/
public static void initSDK(String jsonPath, String dataUrl,String appName) throws IOException {
InputStream serviceAccount = Thread.currentThread().getContextClassLoader().getResourceAsStream(jsonPath);
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.setDatabaseUrl(dataUrl).build();
//初始化firebaseApp
FirebaseApp firebaseApp = FirebaseApp.initializeApp(options);
//存放
firebaseAppMap.put(appName,firebaseApp);
}
/**
* 單設(shè)備推送
* @param appName 應(yīng)用的名字
* @param token 注冊token
* @param title 推送題目
* @param bady 推送內(nèi)容
* @return
* @throws IOException
* @throws FirebaseMessagingException
*/
public static void pushSingle(String appName, String token, String title, String body) throws IOException, FirebaseMessagingException{
//獲取實例
FirebaseApp firebaseApp = firebaseAppMap.get(appName);
//實例為空的情況
if (firebaseApp == null) {
return;
}
//構(gòu)建消息內(nèi)容
Message message = Message.builder().setNotification(new Notification(title,body))
.setToken(token)
.build();
//發(fā)送后青团,返回messageID
String response = FirebaseMessaging.getInstance(firebaseApp).send(message);
System.out.println("單個設(shè)備推送成功 : "+response);
}
/**
* 給設(shè)備訂閱主題
* @param appName 應(yīng)用的名字
* @param tokens 設(shè)備的token,最大1000個
* @param topic 要添加的主題
* @return
* @throws FirebaseMessagingException
* @throws IOException
*/
public static void registrationTopic(String appName, List<String> tokens, String topic) throws FirebaseMessagingException, IOException {
//獲取實例
FirebaseApp firebaseApp = firebaseAppMap.get(appName);
//實例不存在的情況
if(firebaseApp == null) {
return;
}
//訂閱譬巫,返回主題管理結(jié)果對象。
TopicManagementResponse response = FirebaseMessaging.getInstance(firebaseApp).subscribeToTopic(tokens, topic);
System.out.println("添加設(shè)備主題督笆,成功:" + response.getSuccessCount() + ",失斅簟:" + response.getFailureCount());
}
/**
* 按主題推送
* @param appName 應(yīng)用的名字
* @param topic 主題的名字
* @param title 消息題目
* @param body 消息體
* @return
* @throws FirebaseMessagingException
* @throws IOException
*/
public static void sendTopicMes(String appName, String topic, String title, String body) throws FirebaseMessagingException, IOException {
//獲取實例
FirebaseApp firebaseApp = firebaseAppMap.get(appName);
//實例不存在的情況
if(firebaseApp == null) {
return;
}
//構(gòu)建消息
Message message = Message.builder()
.setNotification(new Notification(title,body))
.setTopic(topic)
.build();
//發(fā)送后,返回messageID
String response = FirebaseMessaging.getInstance(firebaseApp).send(message);
System.out.println("主題推送成功: " + response);
}
}
第七步:編寫FCM推送Controller
一共兩個Controller娃肿,單推和群推烟零。
import com.google.firebase.messaging.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import java.io.*;
import java.util.*;
@Api(value = "FireBase", description = "FireBase")
@RequestMapping("/")
@RestController
@Slf4j
public class FireBaseController {
//渠道名字,也是APP的名字
public static String appName = "FCM后臺新增的項目名稱";
@ApiOperation(value = "批量FireBase推送", notes = "批量FireBase推送")
@ApiImplicitParam(name = "fcmSendMessageReqDTO", value = "{\"title\":\"測試標(biāo)題\",\"body\":\"測試內(nèi)容\",\"tokens\":\"1,2\"}")
@PostMapping(value = "/pushFireBaseAll", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public void pushFireBaseAll(@RequestBody FCMSendMessageReqDTO fcmSendMessageReqDTO) {
log.info("進(jìn)入批量FireBase推送 pushFireBaseAll:[{}]", fcmSendMessageReqDTO.toString());
//添加tokens
List<String> tokens = Arrays.asList(fcmSendMessageReqDTO.getTokens().split(","));
//設(shè)置Java代理,端口號是代理軟件開放的端口咸作,這個很重要锨阿。
System.setProperty("proxyHost", "127.0.0.1");
System.setProperty("proxyPort", "8081");
//如果FirebaseApp沒有初始化
if (!FireBaseUtil.isInit(appName)) {
String jsonPath = "fcm/xxxx-firebase-adminsdk.json";
String dataUrl = "https://xxxx.firebaseio.com/";
//初始化FirebaseApp
try {
FireBaseUtil.initSDK(jsonPath, dataUrl, appName);
FireBaseUtil.registrationTopic(appName, tokens, fcmSendMessageReqDTO.getTopic()); //設(shè)置主題
FireBaseUtil.sendTopicMes(appName, fcmSendMessageReqDTO.getTopic(), fcmSendMessageReqDTO.getTitle(), fcmSendMessageReqDTO.getBody()); //按主題推送
} catch (Exception e) {
e.printStackTrace();
}
} else {
log.info("如果FirebaseApp已經(jīng)初始化");
try {
FireBaseUtil.registrationTopic(appName, tokens, fcmSendMessageReqDTO.getTopic()); //設(shè)置主題
FireBaseUtil.sendTopicMes(appName, fcmSendMessageReqDTO.getTopic(), fcmSendMessageReqDTO.getTitle(), fcmSendMessageReqDTO.getBody()); //按主題推送
} catch (IOException e) {
e.printStackTrace();
} catch (FirebaseMessagingException e) {
e.printStackTrace();
}
}
}
@ApiOperation(value = "FireBase推送", notes = "FireBase推送")
@ApiImplicitParam(name = "fcmSendMessageReqDTO", value = "{\"title\":\"測試標(biāo)題\",\"body\":\"測試內(nèi)容\",\"tokens\":\"1\"}")
@PostMapping(value = "/pushFireBase", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public void pushFireBase(@RequestBody FCMSendMessageReqDTO fcmSendMessageReqDTO) {
log.info("進(jìn)入批量FireBase推送 pushFireBaseAll:[{}]", fcmSendMessageReqDTO.toString());
//添加tokens
List<String> tokens = Arrays.asList(fcmSendMessageReqDTO.getTokens().split(","));
//設(shè)置Java代理,端口號是代理軟件開放的端口,這個很重要记罚。
System.setProperty("proxyHost", "127.0.0.1");
System.setProperty("proxyPort", "8081");
//如果FirebaseApp沒有初始化
if (!FireBaseUtil.isInit(appName)) {
String jsonPath = "fcm/xxxx-firebase-adminsdk.json";
String dataUrl = "https://xxxx.firebaseio.com/";
//初始化FirebaseApp
try {
FireBaseUtil.initSDK(jsonPath, dataUrl, appName);
FireBaseUtil.pushSingle(appName, tokens.get(0), fcmSendMessageReqDTO.getTitle(), fcmSendMessageReqDTO.getBody()); //單推
} catch (Exception e) {
e.printStackTrace();
}
} else {
log.info("如果FirebaseApp已經(jīng)初始化");
try {
FireBaseUtil.pushSingle(appName, tokens.get(0), fcmSendMessageReqDTO.getTitle(), fcmSendMessageReqDTO.getBody()); //單推
} catch (IOException e) {
e.printStackTrace();
} catch (FirebaseMessagingException e) {
e.printStackTrace();
}
}
}
}
Spring Boot 項目中集成了swagger 墅诡,小伙伴在使用的時候可以去掉。代碼是沒問題的桐智,都已經(jīng)測試過末早。
總結(jié)
1、一定要確認(rèn)網(wǎng)絡(luò)是否可以FQ说庭,如果不行是訪問不了firebase service的然磷。否則會報以下錯誤,如圖所示:
2刊驴、xxx.json 存放位置的目錄要做好引用姿搜,小編是用mac運行的,如果是windows獲取json文件的相對路徑會有問題捆憎。
3舅柜、給特定設(shè)備推送消息時,需要提前獲取到設(shè)備的deviceToken躲惰。
總之致份,以上代碼可以幫助大家快速入門,并且編寫出可以推送成功的示例础拨,如果在使用中有什么問題大家可以留言一起討論哦~~~
4氮块、國產(chǎn)手機(jī)一般閹割了Google service的服務(wù)绍载,需要自己去找第三方資源安裝,會產(chǎn)生以下問題:
A滔蝉、應(yīng)用“殺死”后基本收不到消息击儡。
B、Android8 系統(tǒng)不管應(yīng)用是否可用都出現(xiàn)收不到消息問題锰提。
有資源的小伙伴可以多測試集中機(jī)型哦曙痘。
5芳悲、設(shè)備必須是android4.0以上立肘,Google Play Services 必須是 11.2.0以上版本(這個沒有測試,擁有的資源基本都是6以上)名扛。