應(yīng)工作項目要求总棵,最近在項目中集成了谷歌目前推薦的推送方式FCM。在此做一些集成過程的記錄與一些注意點的分享搔确,寫的不好請輕噴竟秫。
原本谷歌原生的推送方式是GCM娃惯,也就是Google Cloud Messaging。截至2018年4月10日肥败,Google已棄用GCM趾浅。GCM服務(wù)器和客戶端API已棄用,將于2019年4月11日刪除馒稍。將GCM應(yīng)用程序遷移到 Firebase 云消息傳遞(FCM)皿哨,后者繼承了可靠且可擴展的GCM基礎(chǔ)架構(gòu)以及許多新功能。
簡明的說就是FCM是GCM的升級版纽谒。
Firebase提供了很多服務(wù)证膨,包含但不限于認證、通知鼓黔、分析央勒、AdMod、性能監(jiān)控等澳化。這里我們只對通知進行集成订歪。
如果想了解原來的GCM可以在下面這個文檔中進行查看:
https://developers.google.com/cloud-messaging/
事先聲明下,文章中除了Demo地址是在github上不需要翻墻肆捕,其他地址都需要翻墻才可以打開。
一盖高、先來說說為什么要是使用FCM事先消息推送功能慎陵。
做Android開發(fā)的都知道,我們最煩產(chǎn)品和我們說為什么我APP殺死了推送就收不到了喻奥?為什么IOS就可以席纽?你們能做成像IOS那樣嗎?煩不勝煩撞蚕。想掄拳頭不润梯?忍住,我們是有涵養(yǎng)的程序員甥厦,不用計較無知的PM的言語纺铭。
現(xiàn)在Android系統(tǒng)對后臺進行的管理越來越嚴格,各種定制系統(tǒng)也對后臺進程進行了各種各樣的限制刀疙〔芭猓基本上APP被殺死后,基本都收不到消息推送了∏恚現(xiàn)在的國內(nèi)第三方推送也想了各種辦法去處理殺死進程后收不到推送的問題竟纳,但是效果都不是很好撵溃。
為啥IOS的系統(tǒng)就能那么穩(wěn)定的接收消息推送呢?無論是App在前臺運行時的消息接收锥累,還是App在后臺或者殺死狀態(tài)下缘挑,對離線消息的接收都十分的靠譜。因為每一個蘋果可以通過自家服務(wù)器維持一個長鏈接桶略,每一個iOS的推送都必須和蘋果打交道语淘,所以這個過程控制得很好。
這里就要說為啥我們要用FCM了删性。其實Google也有自己的一套推送服務(wù) 亏娜,就是過去的GCM,現(xiàn)在升級為FCM蹬挺,境外的產(chǎn)品基本都是通過Google去實現(xiàn)的推送维贺。Google提供的推送服務(wù)其實也可以做到IOS那樣的效果,也能做到離線消息的穩(wěn)定接收巴帮。所以如果你做得是一款海外的項目溯泣,那么你就可以選擇通過FCM來實現(xiàn)推送,高效且穩(wěn)定榕茧。國內(nèi)因為墻的存在導致了我們需要去選用各種第三方的推送垃沦。在這期盼哪天Google回歸大陸吧。
嘰嘰歪歪這么多現(xiàn)在開始看如何進行FCM的集成吧。
二握巢、FCM的集成
先提供給大家集成文檔的鏈接豆巨,以及Demo工程的github的鏈接。
集成文檔:https://firebase.google.com/docs/cloud-messaging/
官方Demo地址:https://github.com/firebase/quickstart-android
項目中有很多的Firebase提供的服務(wù)的Demo池充,其中messaging文件夾下就是推送的Demo工程
1.前提條件
集成FCM是有前提條件的,也就是因為這些前提條件導致的境內(nèi)項目無法正常的使用Google提供的服務(wù)缎讼。
Android系統(tǒng)必須是 Android 4.0 (Ice Cream Sandwich) 或更高版本
手機安裝了Google Play 服務(wù) 15.0.0 或更高版本(導致境內(nèi)不可使用的根本原因)
-
Android SDK Manager 必須有Google Play services SDK(該條件貌似用AS開發(fā)時不是必要的收夸,我本人就沒有做到這點)。想要裝的可以根據(jù)下圖指示進行安裝血崭。藍色選中部分就是卧惜。添加Google Play services SDK.png
AndroidStudio 1.5以上版本,最好是用最新的版本
你的手機的網(wǎng)絡(luò)是翻過墻的夹纫,不然是接收不到消息推送的
2.在控制臺配置項目
與很過國內(nèi)的第三方推送集成一樣咽瓷,需要在平臺上添加你的項目,獲取一些初始化需要的東西舰讹。Firebase控制臺忱详。至于注冊賬號什么的我就不說了,我直接說項目的創(chuàng)建跺涤。
登錄控制臺后可以看到這個頁面匈睁,點擊添加項目
添加項目窗口
創(chuàng)建好項目后监透,點擊項目進入到項目的控制臺頁面。
看到了這三個圖標了嗎航唆?我們選擇Android圖標胀蛮,開始對我們現(xiàn)在的Android工程進行關(guān)聯(lián)。這里不得不說FCM還是很強大的糯钙,不僅僅支持Android的推送粪狼,同樣IOS和Web的推送也支持。
關(guān)聯(lián)我們的Android應(yīng)用的步驟展示
照著頁面的輸入框進行輸入任岸,比較無腦再榄。至于SHA-1碼的獲取,自己去google或者找度娘吧享潜。
說一個注意點
當我們關(guān)聯(lián)好了我們的應(yīng)用后艺蝴,如果對應(yīng)用信息進行了修改猬腰,比如SHA-1碼的修改。修改后google-services.json需要重新下載猜敢,覆蓋本地的那一份姑荷。
3.添加SDK
該準備的懂準備好了,現(xiàn)在開添加使用的SDK缩擂,也就是配置gradle文件鼠冕。
- project的gradle
buildscript {
dependencies {
// 納入 google-services 插件
classpath 'com.google.gms:google-services:4.0.1'
}
}
allprojects {
repositories {
// Google 的 Maven 代碼庫
google()
}
}
- app的gradle
dependencies {
implementation 'com.google.firebase:firebase-core:16.0.3'
implementation 'com.google.firebase:firebase-messaging:17.3.1'
// 如果使用到了FirebaseInstanceIdService類,則需要加上這行
implementation 'com.google.firebase:firebase-iid:17.0.1'
// 如果需要在接收消息時配合FirebaseJobDispatcher進行耗時操作撇叁,需要加上這行
implementation 'com.google.firebase:firebase-messaging:17.3.1'
}
// 導入GMS插件,這個插件就是用來解析之前復制到app項目中的google-services.json文件的
apply plugin: 'com.google.gms.google-services'
這里要注意“apply plugin: 'com.google.gms.google-services'”要添加在gradle文件的末尾畦贸,否者會報錯
Please fix the version conflict either by updating the version of the google-services plugin
(information about the latest version is available at https://bintray.com/android/android-tools/com.google.gms.google-services/)
or updating the version of com.google.android.gms to 9.0.0.
4.編寫消息接收服務(wù)
- 一個繼承 FirebaseMessagingService 的服務(wù)陨闹。如果您希望在后臺進行除接收應(yīng)用通知之外的消息處理,則必須添加此服務(wù)薄坏。要在前臺應(yīng)用中接收notification或者dataMessage趋厉,同樣需要編寫此服務(wù)。這個服務(wù)的示例代碼在Demo中有胶坠。我這里簡要的貼一些主要代碼君账。
/**
* FCM 消息接收服務(wù)
* 推送分為 dataMessage(數(shù)據(jù)消息)和notification(通知消息)兩種
* 區(qū)別在于:
* 1.無論應(yīng)用程序位于前臺還是后臺,dataMessage(數(shù)據(jù)消息)都會在onMessageReceived()中處理沈善。 數(shù)據(jù)消息是傳統(tǒng)上與GCM一起使用的類型乡数。
* 2.notification(通知消息)僅當應(yīng)用程序位于前臺時椭蹄,才會在onMessageReceived()中接收。 當應(yīng)用程序在后臺時净赴,將顯示自動生成的通知绳矩,不會再onMessageReceived()中接收。
* 當用戶點擊通知時玖翅,他們將返回到應(yīng)用程序翼馆。 包含通知和數(shù)據(jù)有效負載的消息將被視為通知消息。 Firebase控制臺始終發(fā)送通知消息金度。
*/
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
/**
* @param remoteMessage 表示從Firebase Cloud Messaging收到的消息的對象应媚,它包含了接收到的推送的所有內(nèi)容
*/
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.d(TAG, "收到推送 From: " + remoteMessage.getFrom());
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "收到推送 Message data payload: " + remoteMessage.getData());
}
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "收到通知 Message Notification Body: " + remoteMessage.getNotification().getBody());
}
}
/**
* 如果更新了InstanceID令牌,則調(diào)用此方法猜极。
* 當先前令牌的安全性受到損害中姜,則可能更新令牌。
* 最初生成InstanceID令牌時也會調(diào)用此方法魔吐,因此您可以在此處檢索令牌扎筒。
* 該回調(diào)方法可以代替Demo工程中的的MyFirebaseInstanceIDService。Demo工程中FirebaseInstanceIdService這個類也已經(jīng)被廢棄了酬姆。
*/
@Override
public void onNewToken(String token) {
LogUtils.dTag(TAG, "Refreshed token: " + token);
// 可以在這里將用戶的FCM InstanceID令牌與應(yīng)用程序維護的任何服務(wù)器端帳戶關(guān)聯(lián)起來嗜桌。
// sendRegistrationToServer(token);
}
}
5.清單文件注冊service
<!-- 一項繼承 FirebaseMessagingService 的服務(wù)。如果您希望在后臺進行除接收應(yīng)用通知之外的消息處理辞色,
則必須添加此服務(wù)骨宠。要接收前臺應(yīng)用中的通知、接收數(shù)據(jù)有效負載以及發(fā)送上行消息等相满,您必須繼承此服務(wù)层亿。-->
<service android:name=".push.MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
到這里為止,經(jīng)過上面的一系列“sao操作”立美,就可以坐等接收消息了匿又。下面我來介紹下怎么在控制臺發(fā)送消息
5.獲取設(shè)備注冊令牌(token/RegistionID/InstanceID)
在我們的日常開發(fā)中,這個token值一般都是需要上到自己的服務(wù)端的建蹄。這樣才能實現(xiàn)點對點的消息推送碌更。
您的應(yīng)用初次啟動時,F(xiàn)CM SDK 會為客戶端應(yīng)用實例生成一個注冊令牌洞慎。如果您希望定位至單臺設(shè)備或創(chuàng)建設(shè)備組痛单,則可以通過以下的3種方法去獲取token值。
- 1.需要通過繼承
FirebaseInstanceIdService
,在對調(diào)方法中來獲取此令牌劲腿。不過在最新的SDK中旭绒,F(xiàn)irebaseInstanceIdService已經(jīng)被棄用。
我這里還是貼一下示例代碼
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
@Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
// sendRegistrationToServer(refreshedToken);
}
}
- 2.可以在繼承FirebaseMessagingService 類的onNewToken(String token)方法中得到新得token。其實也就是消息處理服務(wù)中的一個方法挥吵。
@Override
public void onNewToken(String token) {
LogUtils.dTag(TAG, "Refreshed token: " + token);
// 可以在這里將用戶的FCM InstanceID令牌與應(yīng)用程序維護的任何服務(wù)器端帳戶關(guān)聯(lián)起來重父。
// sendRegistrationToServer(token);
}
- 可以在你想要獲取token的地方,調(diào)用API進行token的獲取
private void getPushToken() {
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
@Override
public void onComplete(@NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
Log.e(TAG, "獲取token失斈枇印:", task.getException());
return;
}
// 獲取新的token
String token = task.getResult().getToken();
// 將token上傳給服務(wù)端
registerDevice(token);
}
});
}
注冊令牌可能會在發(fā)生下列情況時更改:
應(yīng)用刪除實例 ID
應(yīng)用在新設(shè)備上恢復
用戶卸載/重新安裝應(yīng)用
用戶清除應(yīng)用數(shù)據(jù)
四坪郭、控制臺發(fā)送消息
下面是在控制臺發(fā)送消息的信息錄入頁面:
這里有個高級選項,在高級選項中脉幢,還可以對通知標題進行設(shè)置歪沃,或者設(shè)置自定義數(shù)據(jù)。
五嫌松、關(guān)于FCM消息類型
在消息處理服務(wù)的代碼實例中沪曙,類備注上寫了,消息類型分為兩種萎羔。
- 通知消息液走,有時被視為“顯示消息”。此類消息由 FCM SDK 自動處理贾陷。
- 數(shù)據(jù)消息缘眶,由客戶端應(yīng)用處理。
想要更深入的了解消息類型的話可以查看官方文檔,FCM消息類型
六髓废、FCM使用的坑
- App 在運行的時候巷懈,推送如果有 Notification ,一般也是我們自己去控制的慌洪,所以最終它點擊后的效果顶燕,我們是可以通過 PendingIntent 做部分定制的。
但是如果是在 App 沒有運行的情況下冈爹,就完全歸 FCM 服務(wù)幫你完成這一系列的操作涌攻,它點擊后的效果,只能將你的 App 調(diào)起频伤,并且把你需要的參數(shù)傳遞到你的 SplashActivity(Action 為 android.intent.action.MAIN 的 Activity) 上恳谎。
public class SplashActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (getIntent().getExtras() != null) {
for (String s : getIntent().getExtras().keySet()) {
Log.d("SplashActivity ", s + "--" + getIntent().getExtras().get(s));
// 在官網(wǎng)的發(fā)送notification 使用高級選項可以自定義 鍵值對,最終會在getIntent().getExtras()中獲取到
}
Intent intent = new Intent(this, MessageActivity.class);
startActivity(intent);
}
}
}
所以我們就需要考慮兩種情況下憋肖,數(shù)據(jù)的傳遞已經(jīng)響應(yīng)因痛,這個是需要根據(jù)業(yè)務(wù)來討論的,空聊是沒有意義的瞬哼。
- 如果App在后臺婚肆,F(xiàn)CM的SDK默認會幫你自動處理消息租副。默認的處理形式就是幫你在系統(tǒng)通知欄彈出一個通知坐慰。就我寫帖子時,我還沒有找到如何自定義默認通知樣式的方法。要是有哪位大大知道怎么自定義结胀,麻煩你告訴我下赞咙。