1. 簡介
本快速教程將演示如何使用 Google 的 Firebase Cloud Messaging 向 Web 和移動應用程序發送推送通知。
2. 什麼是 FCM?
Firebase Cloud Messaging,簡稱 FCM,是一種基於雲端的消息服務,提供以下功能:
- 可靠地向移動或 Web 應用程序(以下簡稱“客户端”)發送消息
- 使用主題或訂閲式地址向所有客户端或特定客户端發送消息
- 在服務器應用程序中接收來自客户端的消息
以下是一些該技術的實際應用示例:
- 向特定產品的獨家優惠發送定向消息
- 通知應用程序所有用户某個新功能可用
- 即時通訊/聊天應用程序
- 直接向特定客户發送通知
3. 應用架構
典型的基於 FCM 的應用架構包括服務器、客户端和 FCM 本身:
在本教程中,我們將重點關注此類應用的服務器端。 在我們的案例中,這個服務器將是一個基於 Spring Boot 的服務,它會暴露一個 REST API。 這個 API 允許我們探索我們如何向我們的(希望非常龐大)用户羣發送通知的不同方式:
- 發佈通知到主題
- 發佈通知到特定客户端
- 發佈通知到多個主題
這類應用的核心在於客户端、主題和訂閲的概念。
3.1. 主題
“主題” (topic) 是一種命名實體,它作為通知的中心樞紐,這些通知共享一些屬性。 例如,一個金融應用程序可以使用一個主題來代表其交易的每種資產。 同樣,一個體育應用程序可以使用一個主題來代表每支球隊,甚至是一個特定的比賽。
3.2. 客户端
客户端是指在給定移動設備上安裝或在瀏覽器中運行的我們應用程序的一個實例。為了接收通知,客户端使用適當的 SDK API 調用將其註冊到我們的 Firebase 項目中。
在成功註冊後,客户端將從 Firebase 獲得一個唯一的註冊令牌。通常,此令牌會發送到服務器端,以便用於直接通知。
3.3. 訂閲
訂閲代表客户端與主題之間的關聯。 服務器應用程序使用 API 調用創建新的訂閲,該調用接受一個或多個客户端註冊令牌以及主題名稱。
4. Firebase 項目設置
Firebase 項目充當我們應用程序使用的雲資源的容器。Google 提供免費初始層,允許開發者實驗並僅支付超出可用配額的部分。
因此,要使用 FCM,我們的第一步是使用 Firebase 控制枱 創建一個新的項目。登錄後,我們會進入 Firebase 的主頁:
在這裏,我們可以選擇添加新項目或選擇現有項目。我們選擇前者。這將啓動一個嚮導,收集創建新項目的所需信息。
首先,我們必須為項目命名:
請注意,在已知的名稱下方,有一個生成的內部 ID 按鈕。通常情況下,無需更改它,但如果出於某種原因不喜歡它,可以點擊它並使用不同的 ID。 請注意,雖然項目 ID 具有唯一性,但項目名稱不具有唯一性,這可能會有些令人困惑。
在下一步中,我們可以選擇將分析功能添加到我們的項目。我們將禁用此選項,因為我們不需要它用於本教程。如果需要,稍後可以啓用它。
一旦我們點擊“創建項目”,Firebase 將我們重定向到新創建的項目管理頁面。
5. 生成服務賬户
為了讓服務器端應用程序調用 Firebase 服務,我們需要生成一個新的服務賬户並獲取其憑據。可以通過訪問項目設置頁面並選擇“服務賬户”選項卡來完成此操作。
任何新項目都將包含一個具有行政權限的服務賬户,該賬户可以基本上在項目中執行任何操作。 在這裏我們將使用它進行測試,但實際應用應創建一個具有有限權限的專用服務賬户。 這需要一些 IAM(Google 的身份和訪問管理)知識,並且超出了本教程的範圍。
現在,我們需要點擊“生成新私鑰”按鈕,下載包含調用 Firebase API 所需數據的 JSON 文件。 顯然,我們必須將其存儲在安全位置。
6. Maven 依賴
現在我們已經準備好 Firebase 項目,是時候編寫服務器組件來發送通知。除了用於 MVC 應用程序的常規 Spring Boot 啓動器,我們還需要添加 firebase-admin 依賴項:
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>9.1.1</version>
</dependency>
最新版本的該依賴項可在 Maven Central 上獲取。
7. Firebase Messaging 配置
FirebaseMessaging 類是用於通過 FCM 發送消息的主要外觀類。 由於該類具有線程安全特性,我們將使用@Bean 方法在@Configuration 類中創建一個其單例實例,並將其提供給我們的控制器:
@Bean
FirebaseMessaging firebaseMessaging(FirebaseApp firebaseApp) {
return FirebaseMessaging.getInstance(firebaseApp);
}
這非常簡單,但現在我們需要提供一個 FirebaseApp 實例。 另一種方法是使用沒有參數的 getInstance() 方法,雖然它能完成任務,但無法更改任何默認參數。
為了解決這個問題,我們創建一個新的 @Bean 方法,以創建自定義的 FirebaseApp 實例:
@Bean
FirebaseApp firebaseApp(GoogleCredentials credentials) {
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(credentials)
.build();
return FirebaseApp.initializeApp(options);
}
在此,唯一自定義之處是使用特定的 GoogleCredentials 對象。 FirebaseOptions 的 builder 允許我們調整 Firebase 客户端的其他方面:
- 超時設置
- HTTP 請求工廠
- 針對特定服務的自定義端點
- 線程工廠
最後一部分配置是憑據本身。 我們將創建一個新的 @Bean,該 bean 使用通過配置屬性提供的服務帳户或默認憑據鏈創建 GoogleCredentials 實例:
@Bean
GoogleCredentials googleCredentials() {
if (firebaseProperties.getServiceAccount() != null) {
try (InputStream is = firebaseProperties.getServiceAccount().getInputStream()) {
return GoogleCredentials.fromStream(is);
}
}
else {
// Use standard credentials chain. Useful when running inside GKE
return GoogleCredentials.getApplicationDefault();
}
}
這種方法簡化了在本地機器上進行測試,因為我們可能擁有多個服務帳户文件。我們可以使用標準的 GOOGLE_APPLICATION_CREDENTIALS 環境變量指向正確的文件,但更改它有點麻煩。
8. 向主題發送消息
向主題發送消息需要兩個步驟:構建一個 <em >Message</em> 對象並使用 <em >FirebaseMessaging</em> 的其中一種方法發送它。 <em >Message</em> 實例使用熟悉的 Builder 模式創建:
Message msg = Message.builder()
.setTopic(topic)
.putData("body", "some data")
.build();
一旦我們獲取到一個 Message 實例,我們使用 send() 方法請求其發送:
String id = fcm.send(msg);這裏,fcm 是我們注入到控制器類中的 FirebaseMessaging 實例。send() 返回一個值,該值是 FCM 生成的消息標識符。我們可以使用此標識符進行跟蹤或日誌記錄。
send() 也有異步版本 sendAsync(),它返回一個 ApiFuture 對象。該類繼承了 Java 的 Future,因此我們可以輕鬆地使用它與諸如 Project Reactor 這樣的響應式框架一起使用。
9. 向特定客户端發送消息
正如我們之前提到的,每個客户端都與它相關聯一個唯一的訂閲令牌。我們使用此令牌作為構建 Message 時其“地址”,而不是主題名稱:
Message msg = Message.builder()
.setToken(registrationToken)
.putData("body", "some data")
.build();
對於需要將相同消息發送給多個客户端的情況,我們可以使用 MulticastMessage 和 sendMulticast()。它們的工作方式與單播版本相同,但允許我們在單個調用中將消息發送到最多 500 個客户端:
MulticastMessage msg = MulticastMessage.builder()
.addAllTokens(message.getRegistrationTokens())
.putData("body", "some data")
.build();
BatchResponse response = fcm.sendMulticast(msg);返回的 BatchResponse 包含針對特定客户端生成的消息標識符以及交付過程中的任何錯誤。
10. 向多個主題發送消息
FCM 允許我們指定一個條件來定義消息的目標受眾。 一個 條件 是一個邏輯表達式,它根據客户端訂閲的(或未訂閲的)主題來選擇客户端。 例如,給定三個主題(T1、T2 和 T3),以下表達式將針對訂閲了 T1 或 T2 但未訂閲 T3 的設備:
('T1' in topics || 'T2' in topics) && !('T3' in topics)在這裏,變量 topics 代表了某個客户端訂閲的所有主題。我們可以現在使用構建器中提供的 setCondition() 方法,構建一個滿足該條件的針對客户端的消息:
Message msg = Message.builder()
.setCondition("('T1' in topics || 'T2' in topics) && !('T3' in topics)")
.putData("body", "some data")
.build();
String id = fcm.send(msg);
11. 訂閲客户端至主題
我們使用 <em>subscribeToTopic() (</em> 或其異步變體 <em>subscribeToTopicAsync()</em> 方法來創建訂閲,將客户端與主題關聯起來。該方法接受一個客户端註冊令牌列表和一個主題名稱作為參數:`
fcm.subscribeToTopic(registrationTokens, topic);請注意,與其它消息系統相比,返回的值沒有標識符用於創建的訂閲。 如果需要跟蹤一個客户端訂閲了哪些主題,則必須自行實現。
要取消訂閲客户端,我們使用 unsubscribeFromTopic():
fcm.subscribeToTopic(Arrays.asList(registrationToken), topic);12. 結論
在本教程中,我們演示瞭如何使用 Google 的 Firebase Cloud Messaging 服務將通知消息發送到 Web 和移動應用程序。