前言
React Native現在是越來越火,一套代碼同時構建iOS、Android兩種應用真的是太爽了,而且有了ES6和React的加成開發效率出奇的高。 雖然坑奇多無比但是還是阻擋不了市場對它的熱愛。但是使用React Native也並非總是那麼舒服,尤其涉及到需要用objective-c或者java實現某些原生功能的時候,讓廣大前端出生沒有原生App開發經驗的同學們苦不堪言,但是沒有辦法,硬着頭皮寫下去總比丟工作強。所以React Native開發者們真的是痛並快樂着,爽並糾結着。然而能力就是在這個過程中快速提高的,所以大家加油,現在只不過是黎明前的黑暗!
今天要分享的是我在React Native開發過程中征服的一個小小領域:消息推送。
其實做手機App就繞不開消息推送,沒有消息推送的App就像一個沒有漂亮前台的公司(就像我們公司,嘿嘿),讓人沒有進去看看的慾望。。怎麼可能火呢。
説正經的,雖然我做完React Native版的消息推送之後發現其實也並不難,但是一路上踩過大大小小的坑、嘗試過無數的解決方案,到最終大功告成還是有點小成就感的(笨人獲得滿足感相對比較容易)。實現React Native App的消息推送可預見的難點在於:
- 在眾多的第三方推送服務提供商中選擇哪一個最合適
- 需要同時實現Android和IOS兩個系統的推送,需要對兩個系統的推送機制都非常熟悉
- 哪些部分需要用原生實現,哪些需要用js實現,如何實現橋接
- 如何實現App在後台或者關閉(inactive)狀態下的推送,又如何實現App打開(active)狀態下的消息推送
結下來我就針對以上的難點,並結合實際的項目來詳細分析如何實現消息推送。
選擇服務提供商
市場上的推送服務提供商有很多,比如友盟、極光推送、Leancloud、個推、環信、融雲等等。這麼多的選擇我們不可能都用過,所以應該從哪些方面去考量呢?
- 首先,必須支持React Native。為了驗證這些第三方服務是否支持React Native,沒有特別好的辦法,我只能一個個的看他們的官網文檔。如果文檔裏面都沒有提到React Native那麼果斷放棄(有些廠商都不提供文檔搜索功能,那也不建議選擇,以後出問題都不好找)。
- 推送服務要穩定、可靠、快速,這一點不太好判斷,因為大家都説自己可靠快速,所以需要實際使用後才能判斷。
- 文檔完善、清晰、準確、更新及時,能提供官方的React Native推送Demo。
- 價格合理(我們還是要想辦法為公司省點錢滴)。
下面是我整理的一些主流的推送服務提供商的對比,可能不是那麼完善和客觀,歡迎糾錯,沒有提到的廠商不好意思啦。。
| 服務商 | React Native支持否 | 文檔質量 | 官方Demo | 價格 |
|---|---|---|---|---|
| 友盟 | 是 | 差,放棄 | 未調查 | 未調查 |
| 極光 | 是 | 缺少 | Demo | 價格 |
| Leancloud | 是 | 高 | iOS | 價格 |
| 騰訊信鴿 | 否(未找到相關文檔) | / | / | / |
| 阿里雲推送 | 否(未找到相關文檔) | / | / | / |
| 百度雲推送 | 否(未找到相關文檔) | / | / | / |
| 網易雲信 | 是 | 缺少 | Demo | 價格 |
| 雲巴 | 否(未找到相關文檔) | / | / | / |
| 個推 | 是 | 缺少 | Demo | 價格 |
| 環信 | 否(只支持IM) | / | / | / |
| 融雲 | 否(未找到相關文檔) | / | / | / |
以上所有的廠商裏面只有4家是支持React Native消息推送的,BAT跟商量好了似的都不支持,難道集體看衰React Native嗎?在僅有的4家廠商中,文檔方面只有Leancloud一家是我覺得文檔質量比較好的,其他廠商都缺少接入React Native的相關文檔。個推只有一個小Demo,而且Demo的文檔也很簡陋,所以先排除。極光的Demo應該是做的最好的,star數最多,Demo文檔看起來也寫的挺好的,雖然沒有明碼標價,但是免費版貌似就夠用了,推送次數沒有上限,二十萬條/秒的推送速度也夠用了,做一般應用應該足夠了。網易的Demo看起來也挺完善,文檔也説的過去,關鍵是價格太貴啦,商用版1800/月。。為了只做一個推送不值得,放棄!最後Leancloud是我個人比較喜歡的,因為之前有項目用到過,不管是文檔、SDK的易用性、服務的可靠性和速度還是網站的審美都可以算得上同行中的佼佼者,而且商用版30/天的價格也可以接收(30/天包含了除推送外其他更多的功能和服務)。所以總結下來,只有極光推送和Leancloud值得一用(歡迎極光和Leancloud聯繫我打賞,嘿嘿),由於我的個人偏好,最終選擇了使用Leancloud。
消息推送概念普及
我們在實現具體項目之前還是有必要了解一下消息推送的相應機制和基本概念的,這裏我就不贅述了,歡迎閲讀Leancloud的 消息推送服務總覽。
接入Leancloud
首先我們創建一個React Native項目(本文Demo地址):
react-native init LeancloudPushDemo
並在Leancloud創建一個同名應用,開發版就好:
安裝完成後,我們需要安裝Leancloud推送相關的js sdk:
$ npm install leancloud-storage --save
$ npm install leancloud-installation --save
我們在項目根目錄下創建services文件夾,並在其中添加PushService.js文件,用於管理消息推送的主要邏輯,初始內容如下:
import AV from 'leancloud-storage';
const appId = 'ppdriT1clcnRoda0okCPaB48-gzGzoHsz';
const appKey = 'Qzarq5cMdWzAMjwDW4umWpBL';
AV.init({
appId: appId,
appKey: appKey
});
const Installation = require('leancloud-installation')(AV);
class PushService {
}
export default new PushService();
目前PushService還是一個空的class,稍後我們會逐漸豐富它的功能。
Leancloud的AppId,AppKey可以在如下頁面獲取:
由於iOS、Android推送方式的差異,接下來我們將分別進行實現。
iOS消息推送
在React Native中實現iOS的消息推送相對Android簡單一些,因為官方已經給出了PushNotificationIOS這樣現成的組件。
配置
首先,根據,在iOS 項目中引入 RCTPushNotification,可參考:Linking Libraries - React Native docs
步驟1:將PushNotification項目拖到當前iOS主項目
步驟2:添加libRCTPushNotification靜態庫
步驟3: 開啓Push Notification功能
然後,修改AppDelegate.m,增加推送相關事件代理,可參考:PushNotificationIOS - React Native docs,。
獲取devideToken,更新_Installation表
Leancloud需要根據iOS設備的deviceToken來決定推送到哪台設備,所以需要把deviceToken保存到_Installation表。而保存的最佳時機就在App剛剛啓動的時候,在PushService下添加如下代碼:
//引用PushNotificationIOS
const PushNotificationIOS = require('react-native').PushNotificationIOS;
...
class PushService {
//獲取iOS消息通知權限
_iOS_initPush = () => {
PushNotificationIOS.addEventListener('register', this._iOS_onRegister);
PushNotificationIOS.requestPermissions();
}
//權限獲取成功回調
_iOS_onRegister = (deviceToken) => {
if (deviceToken) {
this._iOS_saveInstallation(deviceToken);
}
}
//保存deviceToken到Installation
_iOS_saveInstallation = (deviceToken) => {
const info = {
apnsTopic: 'com.example',
deviceType: 'ios',
deviceToken: deviceToken
};
Installation.getCurrent()
.then(installation => installation.save(info))
.then(result => console.log(result))
.catch(error => console.error(error))
}
}
...
修改App.js,在componentDidMount時執行初始化:
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import PushService from './services/PushService';
type Props = {};
export default class App extends Component<Props> {
componentDidMount() {
PushService._iOS_initPush();
}
render() {
return (
<View>
<Text>Leancloud Push Demo</Text>
</View>
);
}
}
現在我們來運行一下項目(須使用真機,模擬器獲取不到deviceToken),看是否能獲取到deviceToken並保存。
保存成功後發現_Installation表中多了一條記錄:
推送證書設置
成功保存deviceToken意味着我們已經成功了一半了,但如果要讓iOS設備能收到通知,還需要配置推送證書,詳細步驟請參考 iOS推送證書設置指南。推薦使用Token Authentication。
推送證書設置完成之後,就可以測試手機是否能收到消息通知了。Leancloud提供在線發送消息的功能:
在線發送之後,手機就可以收到通知了(不小心暴露我的起牀時間了。。):
通知的接收和處理
到目前為止我們已經成功了一大半了,但是我們還想做得更多一點,一款成熟的應用還應該包括以下功能:
- App在前台、後台運行或者關閉狀態都能看到通知消息
- App在後台或者關閉狀態收到通知,App圖標能顯示通知個數的badge
- 點擊通知能夠進行自定義的操作,比如跳轉到具體頁面
App打開時通知的顯示
當App在前台運行時收到通知iOS默認是不會提醒的(iOS 10開始支持在前台顯示,請參考 stackoverflow),因此我們需要自己實現接收通知並顯示的邏輯。
我們選擇用 react-native-message-bar來展示通知,先安裝react-native-message-bar:
npm install react-native-message-bar --save
然後,在App.js中引入並註冊MessageBar:
...
const MessageBarAlert = require('react-native-message-bar').MessageBar;
const MessageBarManager = require('react-native-message-bar').MessageBarManager;
...
componentDidMount() {
PushService._iOS_initPush();
MessageBarManager.registerMessageBar(this.refs.alert);
}
componentWillUnmount() {
PushNotificationIOS.removeEventListener('register');
MessageBarManager.unregisterMessageBar();
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 24 }}>Leancloud Push Demo</Text>
<MessageBarAlert ref="alert" />
</View>
);
}
...
接着,修改PushService,增加對notification事件的監聽,和顯示自定義Alert的方法:
...
_iOS_initPush = () => {
PushNotificationIOS.addEventListener('register', this._iOS_onRegister);
PushNotificationIOS.addEventListener('notification', this._iOS_onNotification);
PushNotificationIOS.requestPermissions();
}
_iOS_onNotification = (notification) => {
//如果app在前台則顯示alert
if (AppState.currentState === 'active') {
this._showAlert(notification._alert);
}
}
...
_showAlert = (message) => {
const MessageBarManager = require('react-native-message-bar').MessageBarManager;
MessageBarManager.showAlert({
title: '您有一條新的消息',
message: message,
alertType: 'success',
stylesheetSuccess: {
backgroundColor: '#7851B3',
titleColor: '#fff',
messageColor: '#fff'
},
viewTopInset : 20
});
}
...
最後重新運行App,然後在線發送一條通知,App打開狀態下也能顯示通知了:
收到通知顯示Badge
要實現badge顯示並能隨着通知個數遞增非常簡單,只需要在Leancloud控制枱中勾選Increment iOS badges,然後發送通知後App圖標上就會出現紅色的badge了:
清除badge
添加badge之後,我們需要在合適的時間點再將其清除,Leancloud將每個設備上badge的數量也保存在_Installation表中,所以清除設備的badge同時需要修改Installation表:
_iOS_cleanBadge = () => {
Installation.getCurrent()
.then((installation) => {
installation.set('badge', 0);
return installation.save();
})
.then((result) => {
PushNotificationIOS.setApplicationIconBadgeNumber(0);
})
.catch(error => console.log(error));
}
自定義點擊通知的行為
點擊通知又分為點擊iOS系統彈出的通知提醒和點擊我們自定義的MessageBar。而點擊iOS系統的通知又可分為App在後台運行和App處於關閉狀態。接下來我們就分別討論這三種狀態下如何處理:
1. App打開,點擊MessageBar
react-native-message-bar提供了onTapped的callback,所以我們只需要傳入我們想要執行的方法就行了,我們將PushService進行如下修改:
...
_iOS_onNotificationTapped = () => {
Alert.alert('Notification Tapped');
}
_showAlert = (message) => {
const MessageBarManager = require('react-native-message-bar').MessageBarManager;
MessageBarManager.showAlert({
...
onTapped: this._iOS_onNotificationTapped
});
}
...
2. App在後台運行,點擊系統通知
實現思路是,當app在後台運行時收到通知,點擊通知會觸發notification事件,我們用一個臨時變量記錄下當前的通知,再通過監聽app狀態的變化,當app從後台切換到前台後臨時變量是否有值判斷是否是點擊通知打開的app,如果是通過點擊通知打開app,執行我們想要的邏輯。説的有點繞,讓我們來看代碼:
...
class PushService {
//用於記錄通知的臨時變量
backgroundNotification = null;
_iOS_initPush = () => {
...
//監聽app狀態的改變
AppState.addEventListener('change', (newState) => {
if (newState === 'active') {
if (this.backgroundNotification != null) {
this._iOS_onNotificationTapped();
this.backgroundNotification = null;
this._iOS_cleanBadge();
}
}
});
}
...
_iOS_onNotification = (notification) => {
...
} else if (AppState.currentState === 'background') {
//app在後台運行時點擊通知
this.backgroundNotification = notification;
}
}
...
3. App關閉狀態下,點擊系統通知
直接調用PushNotificationIOS.getInitialNotification判斷app關閉時,是否通過點擊系統消息打開:
...
_iOS_initPush = () => {
...
//app關閉時,是否通過點擊系統通知打開
PushNotificationIOS.getInitialNotification()
.then((notification) => {
if (notification) {
this._iOS_onNotificationTapped();
}
});
}
...
結語
至此,使用Leancloud實現iOS的消息推送已實現完成,並涵蓋了主要的應用場景。出於控制篇幅的原因,Android的實現單獨寫了一篇文章分享給大家:
Android篇地址:使用Leancloud實現React Native App的消息推送(Push Notification)- Android篇
本文Demo Github地址:https://github.com/MudOnTire/LeancloudPushDemo,如果對你有幫助,star一下吧。