FCM Message
共通
- Message data type
- Notification Message
- 長度限制 2KB
- 訊息接收狀況
- 若裝置位於app畫面
- 不顯示notification,會觸發接收function
- 若app已執行,在背景執行 or app尚未開啟
- 顯示notification,不會觸發接收function
- 若裝置位於app畫面
- Data Message
- 長度限制 4KB
- 訊息接收狀況
- 無論開啟與否,皆會觸發接收function
- Notification Message
- 訊息的種類
- Downstream Message
- Topic Message
- 可註冊的topic數量不受限制
- 適用於公開的資訊,像是新聞、氣象等消息
- 針對資料量做優化而非延遲時間,若需要更快且安全的傳遞方式,請選擇以token為對象的訊息種類而非topic
- 若需要送給每個user的不同裝置時,請考慮Device Group messaging
- 使用者於APP端subscribe topic,若topic不存在則會被建立
- topic建立後最多一天的時間,在firebase console上才會出現在列表上
- Device Group Message
- 係指單個User的多個不同的Device
- 讓所有的device共用一個notification key
- 長度限制: 在ios device為2KB,其他平台為4KB
- Topic Message
- Upstream Message
- 當server使用XMPP Connection Server protocol時才可運用
- Downstream Message
- 訊息的生命週期
- FCM通常在發送消息後立即傳遞消息。然而,這可能並不總是可能的。例如,如果平台是Android,則設備可以被關閉,離線或以其他方式不可用。 FCM可能會故意延遲郵件,以防止應用程序佔用過多的資源和負面影響電池壽命。
- 當這種情況發生時,FCM存儲消息並在可行時盡快發送。雖然這在大多數情況下都很好,但有些應用程序可能不會傳遞延遲的消息。例如,如果消息是來電或視頻聊天通知,則僅在呼叫終止之前的短時間段內是有意義的。或者如果消息是對事件的邀請,則在事件結束之後接收到消息是無用的。
- 您可以使用time_to_live參數(在HTTP和XMPP請求中都支持)來指定消息的最大生命週期。此參數的值必須是從0到2,419,200秒的持續時間,並且它對應於FCM存儲和嘗試傳遞消息的最大時間段。不包含此字段的請求的最大期限為四周。
- 指定消息的生命週期的另一個優點是,FCM從不限制time_to_live(TTL)值為0秒的消息。換句話說,FCM保證對於必須“現在或從不”傳遞的消息的最大努力。請記住,time_to_live值為0表示不能立即傳遞的郵件將被丟棄。然而,因為這樣的消息從未被存儲,這提供了用於發送通知消息的最佳等待時間。
- 當app server丟出訊息且收到回傳messageID並不代表訊息已經傳到client端,僅代表該訊息的資料是可以被受理的
- 在最佳情況下,如果設備連接到FCM,屏幕為開啟狀態,並且沒有節流限制,則會立即傳送消息。
- 如果設備已連接但處於打盹模式,FCM將存儲低優先級消息,直到設備脫離打盹模式。 這就是collapse_key標誌起作用的地方:如果已經存在具有相同折疊密鑰(和註冊令牌)的消息並存儲並等待傳遞,則舊消息被丟棄,並且新消息取代它的位置(即,舊的 消息由新的消息折疊)。 但是,如果未設置折疊鍵,則新消息和舊消息都將存儲以供將來交付。
- 如果設備未連接到FCM,則會存儲消息,直到建立連接(再次遵守折疊密鑰規則)。 當建立連接時,FCM將所有掛起的消息傳遞到設備。 如果設備從未再次連接(例如,如果它已恢復出廠設置),則消息最終超時,並從FCM存儲器中丟棄。 默認超時為四周,除非設置time_to_live標誌
- 當FCM嘗試將消息傳遞到設備並卸載應用程序時,FCM立即丟棄該消息,並使註冊令牌無效。 以後嘗試向該設備發送消息將導致NotRegistered錯誤。
- client端token可能會改變的狀況
- APP端刪除該token
- APP在新裝置上還原
- 使用者移除/重新安裝APP
- 清除APP資料
android
接收狀況統整圖
步驟如下 (https://firebase.google.com/docs/cloud-messaging/android/client)
- 在firebase console建立專案
- 加入android項目,設定keystore SHA1值
- 下載 google-services.json 放到 {ProjectRoot}/app 底下
- 專案:build.gradle(Project)加入:
dependencies { compile 'com.google.firebase:firebase-messaging:10.0.0' }
建立繼承 FirebaseMessagingService 的 Service (例如:MyFirebaseMessagingService)並在AndroidManifest.xml加入:
<service android:name=".MyFirebaseMessagingService"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT"/> </intent-filter> </service>
建立繼承 FirebaseInstanceIdService 的 Service (例如:MyFirebaseInstanceIDService)並在AndroidManifest.xml加入:
<service android:name=".MyFirebaseInstanceIDService"> <intent-filter> <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/> </intent-filter> </service>
API參考
ios
- 接收狀況:
- 若APP完全未開啟則無法收到訊息
- (ios-10.1.1-iphone6)若APP已開啟
- 在背景執行時,收到notification message,會觸發AppDelegate.didReceiveRemoteNotification(),點擊通知才會呼叫AppDelegate.userNotificationCenter()
- 在背景執行時,收到data message,只觸發AppDelegate.didReceiveRemoteNotification()
- 若APP正在執行,皆可接收訊息,但notification不會顯示,若為notification message,則只有AppDelegate.userNotificationCenter()會觸發
- (ios-9.3.5-iphone4s)
步驟: (https://firebase.google.com/docs/cloud-messaging/ios/client)
- 在Firebase console加入app,下載設定檔(GoogleService-Info.plist) 放到 {project-root}
- 在Project中加入SDK (open terminal)
- 建立podFile
$ cd {project-root} $ pod init
- 編輯podFile並加入:
pod 'Firebase/Core' pod 'Firebase/Messaging'
- 安裝pods
$ pod install $ open {.xcworkspace in project-root}
- 安裝完後須在"Build Setting"->"Framework Search Path"加入“$(inherited)”,不然會出現framwork not found的問題
- 步驟會為您的應用程式建立 .xcworkspace 檔案。應用程式日後的所有開發作業都將使用這個檔案
- 建立podFile
APNs相關設定
- 製作憑證 (https://firebase.google.com/docs/cloud-messaging/ios/certs#create_the_ssl_certificate)
- 需為apple developer(US$99/year),若加入group,權限不為member (admin以上)
- 上傳至Firebase console
- 製作Provisioning Profile
- 安裝到Xcode後該機器的APNs服務才有效用
- 製作憑證 (https://firebase.google.com/docs/cloud-messaging/ios/certs#create_the_ssl_certificate)
在APP中初始化Firebase (Swift)
在AppDelegate類別中加入以下程式碼
import UIKit import Firebase //<==here @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { FIRApp.configure() //<==here return true } }
註冊notification
在 AppDelegate 類別中加入以下程式碼 (https://github.com/firebase/quickstart-ios/blob/master/messaging/MessagingExampleSwift/AppDelegate.swift#L33-L51)
if #available(iOS 10.0, *) { let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization( options: authOptions, completionHandler: {_, _ in }) // For iOS 10 display notification (sent via APNS) UNUserNotificationCenter.current().delegate = self //will be error here // For iOS 10 data message (sent via FCM) FIRMessaging.messaging().remoteMessageDelegate = self //will be error here } else { let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil) application.registerUserNotificationSettings(settings) } application.registerForRemoteNotifications()
解決上述error需在 AppDelegate 後面(class scope外)加入兩個extension
@available(iOS 10, *) extension AppDelegate : UNUserNotificationCenterDelegate { // Receive displayed notifications for iOS 10 devices. func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { let userInfo = notification.request.content.userInfo // Print message ID. print("Message ID: \(userInfo["gcm.message_id"]!)") // Print full message. print(userInfo) } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo // Print message ID. print("Message ID: \(userInfo["gcm.message_id"]!)") // Print full message. print(userInfo) } }
extension AppDelegate : FIRMessagingDelegate { // Receive data message on iOS 10 devices while app is in the foreground. func applicationReceivedRemoteMessage(_ remoteMessage: FIRMessagingRemoteMessage) { print(remoteMessage.appData) } }
Server
推播訊息 (HTTP-POST) (https://firebase.google.com/docs/cloud-messaging/http-server-ref#send-downstream)
- url: https://fcm.googleapis.com/fcm/send
- Header:
- Authorization:{Firebase Cloud Messaging token}
- Content-Type:application/json
- Body(JSONObject):
- 對象:
- "to" : "one token" or "topic" or "condition"
- "registration_ids" : "more than one token" (should be JSONArray)
- 訊息類型
- "data":(JSONObject)
- "notification":(JSONObject)
- 若兩個key同時擁有則視為 notification messgae
- 對象:
Topic相關 (HTTP-GET) (https://developers.google.com/instance-id/reference/server)
- 查詢使用者已註冊的topic
- url: https://iid.googleapis.com/iid/info/{USER_TOKEN}?detail=true
- {USER_TOKEN}:client token
- url: https://iid.googleapis.com/iid/info/{USER_TOKEN}?detail=true
- 為使用者加入topic
- https://iid.googleapis.com/iid/v1/{USER_TOKEN}/rel/topics/{TOPIC_NAME}
- {USER_TOKEN}:client token
- {TOPIC_NAME}:要註冊的topic名稱
- https://iid.googleapis.com/iid/v1/{USER_TOKEN}/rel/topics/{TOPIC_NAME}
- 查詢使用者已註冊的topic