轉載請注明出處 原文鏈接:移動應用消息推送及推送原理
消息推送
消息通知分本地通知和遠程推送通知。
本文是記錄React Native使用aws push notifications推送及相關配置及遇到的相關問題。在twilio和aws中選擇使用aws的Pinpoint服務。Pinpoint服務前一百萬條通知免費,之后每一百萬條通知收取 1.00 USD。SMS發送的每條消息都需要付費。每條消息支付的價格取決於接收人所在國家/地區。APNs使用keys來配置推送。
iOS推送原理
蘋果的推送服務通知由蘋果的推送服務器APNs(Apple Push Notification server)來完成的。APNs服務器收到我們發送的消息,然后將這條消息推送到指定的IOS設備上,然后再由IOS設備通過通知或者聲音的形式推到到我們的應用。
iOS要實現遠程推送需要安裝具有推送的應用,首次打開應用后詢問請求推送權限之后會向APNs服務器進行注冊。注冊成功之后APNs服務器會返回一個deviceToken, 拿到這個deviceToken 我們就可以發消息到這台iOS設備了。當注冊成功之后,我們的應用會和APNs服務器維持一個TCP鏈接。我們將deviceToken會傳到自己的服務器,通過具體的業務需求服務器發送消息到APNs服務器,APNs會將消息推送到iOS設備上。
iOS設備注冊APNs的過程
- 設備連接APNs服務器並攜帶設備序列化(UUID)
- 連接成功,APNs經過打包和處理后生成deviceToken並返回給注冊設備
- 設備獲取的deviceToken通過自己的程序把它發送到自己的服務器
- 設備在APNs服務器與自己的服務器進行注冊
iOS推送過程
- 設備安裝了推送功能的應用,設備在有網下有連接APNs推送的服務器,連接過程中,APNs服務器會驗證deviceToken,成功后維持一個基於TCP的長連接
- 服務器把要推送的消息(含有deviceToken)一起發送給APNs服務器
- APNs服務器將接收到的信息推送給指定的deviceToken設備
- 設備收到推送后通知到應用程序

根據發送過程的流程, 我們可以較快的排查消息推送的一些收不到消息問題。
Android推送原理
Android推送服務是采用GCM(Google Clond Message)來實現的(國內需要自備梯子),系統級別的消息推送服務。
App從服務器獲取最新消息的基本方式有3種:Push、Pull 和 SMS
-
Push 是服務器主送發送到客戶端,客戶端被動接受
-
Pull 客戶端定期主動拉取服務端的消息,也叫做輪詢。 缺點是要消耗設備的CPU,電量,流量等。
-
SMS 通過發送信令的方式發給客戶端,客戶端收到后向服務器拉取消息。缺點就是收費問題。
推送方案
C2DM(Cloudto Device Messaging)
Google 操作系統有自身的消息推送功能(系統級別)
基於Push方式,C2DM服務負責處理諸如消息排隊等事務,並向運行於目標設備上的應用程序分發這些消息。
注冊設備、消息推送流程、出發流程同IOS。
這種C2DM方式是Google官方提供的。國內使用Google需要FQ,國內的手機需要安裝Google。
輪詢
基於Pull的方式。客戶端定期主動拉取服務端的消息。 成本是要消耗設備的CPU,電量,流量等。
SMS
短信發送,基於Push方式。原理是通過攔截SMS消息並且解析消息內容來了解服務器的意圖,並獲取其顯示內容進行處理。成本就是通過運營商繳納短信費用。
MQTT協議
國內很多企業都廣泛使用MQTT作為Android手機客戶端與服務器端推送消息的協議。
MQTT(消息隊列遙測傳輸)是ISO 標准(ISO/IEC PRF 20922)下基於發布/訂閱范式的消息協議。工作在 TCP/IP協議族上。基於Push的方式輕量級的消息發布/訂閱協議。
XMPP協議
Extensible Messaging And Presence protocol,可擴展通訊和表示協議,是基於可擴展標記語言(XML)的協議。
基本的網絡架構:
XMPP中定義了三個角色,客戶端,服務器,網關。通信能夠在這三者的任意兩個之間雙向發生。服務器同時承擔了客戶端信息記錄,連接管理和信息的路由功能。網關承擔着與異構即時通信系統的互聯互通,異構系統可以包括SMS(短信),MSN,ICQ等。基本的網絡形式是單客戶端通過TCP/IP連接到單服務器,然后在之上傳輸XML。
XMPP系統特點:
- 客戶機/服務器通信模式;
- 分布式網絡;
- 簡單的客戶端;
- 標准通用標記語言的子集XML的數據格式。
通俗解釋:其實XMPP 是一種很類似於http協議的一種數據傳輸協議,它的過程就如同“解包裝--〉包裝”的過程,用戶只需要明白它接受的類型,並理解它返回的類型,就可以很好的利用xmpp來進行數據通訊。
XMPP推送的優點:簡單、開源、拓展性強。缺點:消息被推送出去,不管消息是否成功到達客戶端。Android XMPP源碼實例
其他的第三方推送平台
友盟、極光等。
使用aws配置推送
環境配置
先安裝Amplify CLI,用來創建AWS雲服務。
npm install -g @aws-amplify/cli
設置amplify,進行配置Amplify
amplify configure
在命令框執行之后會跳轉網頁來登錄aws網站, 登錄后會創建一個IMA賬號。會需要設置AWS Region和user name。登錄的賬號需擁有創建IMA的權限。否則在創建過程中會提示403 無權操作。
接下來需要初始化app端的配置,進入項目根目錄執行
amplify init
根據提示完成項目相應配置如平台、語言、編輯器、環境等。最后選擇剛剛創建好的IMA角色,會在項目下生成amplify-config文件夾及export—was.js。

IOS平台配置
此時我們可以添加需要的aws服務了。添加Push Notifications服務,可以先從IOS平台開始配置:
amplify add notifications
選擇APNs

會提示需要創建的Pinpoint資源的相關配置,如資源名、蘋果推送證書或者key ID、、bundle identifier、Team identifier、Authenrication key(p8文件 下面介紹如何創建key)。該階段的操作會在aws控制台自動創建好這個服務。(后端開發人員一般就不需要在去拿生成的keys、.p8)。推介使用keys來創建推送服務。
基於keys的身份驗證提供了與APN通信的無狀態方式。無狀態通信比基於證書的通信快,因為它不需要APNs來查找與您的提供程序服務器有關的證書或其他信息。使用基於令牌的身份驗證還有其他優點:
- 可以在同個keys下分發沙河環境和發布狀態的推送(Sandbox Production)
- 可以從多個提供程序服務器中使用相同的令牌
- 可以使用一個令牌來分發公司所有應用程序的通知
aws控制台

這樣APNs服務就在aws Pinpoint服務中配置好了。
創建IOS key步驟
有一個付費的apple賬號之后登陸蘋果開發者中心,進入account

keys 創建key

創建完成

可以下載這個key也就是p8文件(* 只能下載一次,蘋果服務器並且不會對它進行保存,所以需要保存好)
Android平台配置
amplify add notifications

在配置中需要輸入在firebase申請的該App的api秘鑰。配置完成之后,在aws控制台中:
Android的推送需要Google的服務,需要訪問Firebase控制台,然后點擊“ 項目概述”旁邊的Gear圖標,然后點擊“ 項目設置”。

下載google-server.json放到項目adnroid/app/目錄下
在配置推送時遇到的問題
Android 遇到的問題
在項目中一開始使用的是yarn安裝的依賴,在安卓項目中會出現。同時也是這個不經意的問題,導致在配置Android時花費了不少的功夫。google和百度提出的解決方案是把項目生成androidX,


但是在gradle.properties里面的useAndroidX已經是true。按照網上在一頓排錯之后還是沒有解決這個問題, 最后就把整個node_nodules刪除之后清除node_modules緩存,重新npm install就沒有再繼續提示這個錯誤。
import android.support.v4.app.NotificationCompat
配置好,當應用啟動時 android 不會調用PushNotification.onRegister這個方法。因為調用這個方法的目的是獲取設備的deviceToken,所以需要從NativeModules.RNPushNotification模塊中獲取相應的方法來獲取deviceToken。
在ReactNative中配置
注意: 在配置ios推送時需要申請推送權限請求。
IOS 配置
// Required to register for notifications
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
[RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];
}
// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
[RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// IOS 10+ Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler
{
[RNCPushNotificationIOS didReceiveNotificationResponse:response];
completionHandler();
}
// IOS 4-10 Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[RNCPushNotificationIOS didReceiveLocalNotification:notification];
}
Android
打開android / app / src / main / AndroidManifest.xml文件,並將以下配置添加到applicationelement中。
<application ... >
<!--[START Push notification config -->
<!-- [START firebase_service] -->
<service
android:name="com.amazonaws.amplify.pushnotification.RNPushNotificationMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<!-- [END firebase_service] -->
<!-- [START firebase_iid_service] -->
<service
android:name="com.amazonaws.amplify.pushnotification.RNPushNotificationDeviceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<receiver
android:name="com.amazonaws.amplify.pushnotification.modules.RNPushNotificationBroadcastReceiver"
android:exported="false" >
<intent-filter>
<action android:name="com.amazonaws.amplify.pushnotification.NOTIFICATION_OPENED"/>
</intent-filter>
</receiver>
<!-- [END Push notification config -->
</application>
App

//...
constructor(props) {
super(props);
if (isAndroid) {
//Android 不會調用 PushNotification.onRegister,需要從NativeModules中調用RNPushNotification.getToken來回去deviceToken
NativeModules.RNPushNotification.getToken(this.handleRegisterDeviceToken);
} else {
PushNotification.onRegister(this.handleRegisterDeviceToken);
}
// 設置收到notification的回調 處理收到的消息
PushNotification.onNotification(notification => {
this.handleReceiveNotification(notification);
if (isIos) {
notification.finish(PushNotificationIOS.FetchResult.NoData);
}
});
// 點擊消息后的回調
PushNotification.onNotificationOpened(this.handleOpenNotification);
}
//...
到此 我們的客戶端配置好了aws的推送接受服務。
當拿到deviceToken我們需要將它發給我們的服務器,具體的業務邏輯交給后端(接入aws-pinpoint服務)去觸發消息推送。
總結
在iOS推送過程中,deviceToken是會變化的問題, 所以在程序中要做好更新deviceToken的動作,避免因設備升級或者應用重裝而導致收不到消息。
參考鏈接:
轉載注明出處:原文鏈接:移動應用消息推送及推送原理
