應(yīng)工作項目要求,最近在項目中集成了谷歌目前推薦的推送方式FCM。在此做一些集成過程的記錄與一些注意點的分享,寫的不好請輕噴。
原本谷歌原生的推送方式是GCM,也就是Google Cloud Messaging。截至2018年4月10日,Google已棄用GCM。GCM服務(wù)器和客戶端API已棄用,將于2019年4月11日刪除。將GCM應(yīng)用程序遷移到 Firebase 云消息傳遞(FCM),后者繼承了可靠且可擴(kuò)展的GCM基礎(chǔ)架構(gòu)以及許多新功能。
簡明的說就是FCM是GCM的升級版。
Firebase提供了很多服務(wù),包含但不限于認(rèn)證、通知、分析、AdMod、性能監(jiān)控等。這里我們只對通知進(jìn)行集成。
如果想了解原來的GCM可以在下面這個文檔中進(jìn)行查看:
https://developers.google.com/cloud-messaging/
事先聲明下,文章中除了Demo地址是在github上不需要翻墻,其他地址都需要翻墻才可以打開。
一、先來說說為什么要是使用FCM事先消息推送功能。
做Android開發(fā)的都知道,我們最煩產(chǎn)品和我們說為什么我APP殺死了推送就收不到了?為什么IOS就可以?你們能做成像IOS那樣嗎?煩不勝煩。想掄拳頭不?忍住,我們是有涵養(yǎng)的程序員,不用計較無知的PM的言語。
現(xiàn)在Android系統(tǒng)對后臺進(jìn)行的管理越來越嚴(yán)格,各種定制系統(tǒng)也對后臺進(jìn)程進(jìn)行了各種各樣的限制?;旧螦PP被殺死后,基本都收不到消息推送了。現(xiàn)在的國內(nèi)第三方推送也想了各種辦法去處理殺死進(jìn)程后收不到推送的問題,但是效果都不是很好。
為啥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)因為墻的存在導(dǎo)致了我們需要去選用各種第三方的推送。在這期盼哪天Google回歸大陸吧。
嘰嘰歪歪這么多現(xiàn)在開始看如何進(jì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是有前提條件的,也就是因為這些前提條件導(dǎo)致的境內(nèi)項目無法正常的使用Google提供的服務(wù)。
Android系統(tǒng)必須是 Android 4.0 (Ice Cream Sandwich) 或更高版本
手機(jī)安裝了Google Play 服務(wù) 15.0.0 或更高版本(導(dǎo)致境內(nèi)不可使用的根本原因)
-
Android SDK Manager 必須有Google Play services SDK(該條件貌似用AS開發(fā)時不是必要的,我本人就沒有做到這點)。想要裝的可以根據(jù)下圖指示進(jìn)行安裝。藍(lán)色選中部分就是。添加Google Play services SDK.png
AndroidStudio 1.5以上版本,最好是用最新的版本
你的手機(jī)的網(wǎng)絡(luò)是翻過墻的,不然是接收不到消息推送的
2.在控制臺配置項目
與很過國內(nèi)的第三方推送集成一樣,需要在平臺上添加你的項目,獲取一些初始化需要的東西。Firebase控制臺。至于注冊賬號什么的我就不說了,我直接說項目的創(chuàng)建。
登錄控制臺后可以看到這個頁面,點擊添加項目

添加項目窗口

創(chuàng)建好項目后,點擊項目進(jìn)入到項目的控制臺頁面。

看到了這三個圖標(biāo)了嗎?我們選擇Android圖標(biāo),開始對我們現(xiàn)在的Android工程進(jìn)行關(guān)聯(lián)。這里不得不說FCM還是很強(qiáng)大的,不僅僅支持Android的推送,同樣IOS和Web的推送也支持。
關(guān)聯(lián)我們的Android應(yīng)用的步驟展示
照著頁面的輸入框進(jìn)行輸入,比較無腦。至于SHA-1碼的獲取,自己去google或者找度娘吧。



說一個注意點
當(dāng)我們關(guān)聯(lián)好了我們的應(yīng)用后,如果對應(yīng)用信息進(jìn)行了修改,比如SHA-1碼的修改。修改后google-services.json需要重新下載,覆蓋本地的那一份。
3.添加SDK
該準(zhǔn)備的懂準(zhǔn)備好了,現(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進(jìn)行耗時操作,需要加上這行
implementation 'com.google.firebase:firebase-messaging:17.3.1'
}
// 導(dǎo)入GMS插件,這個插件就是用來解析之前復(fù)制到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ù)。如果您希望在后臺進(jìn)行除接收應(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(通知消息)僅當(dāng)應(yīng)用程序位于前臺時,才會在onMessageReceived()中接收。 當(dāng)應(yīng)用程序在后臺時,將顯示自動生成的通知,不會再onMessageReceived()中接收。
* 當(dāng)用戶點擊通知時,他們將返回到應(yīng)用程序。 包含通知和數(shù)據(jù)有效負(fù)載的消息將被視為通知消息。 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)用此方法。
* 當(dāng)先前令牌的安全性受到損害,則可能更新令牌。
* 最初生成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)用程序維護(hù)的任何服務(wù)器端帳戶關(guān)聯(lián)起來。
// sendRegistrationToServer(token);
}
}
5.清單文件注冊service
<!-- 一項繼承 FirebaseMessagingService 的服務(wù)。如果您希望在后臺進(jìn)行除接收應(yīng)用通知之外的消息處理,
則必須添加此服務(wù)。要接收前臺應(yīng)用中的通知、接收數(shù)據(jù)有效負(fù)載以及發(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)用程序維護(hù)的任何服務(wù)器端帳戶關(guān)聯(lián)起來。
// sendRegistrationToServer(token);
}
- 可以在你想要獲取token的地方,調(diào)用API進(jìn)行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è)備上恢復(fù)
用戶卸載/重新安裝應(yīng)用
用戶清除應(yīng)用數(shù)據(jù)
四、控制臺發(fā)送消息
下面是在控制臺發(fā)送消息的信息錄入頁面:

這里有個高級選項,在高級選項中,還可以對通知標(biāo)題進(jìn)行設(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默認(rèn)會幫你自動處理消息。默認(rèn)的處理形式就是幫你在系統(tǒng)通知欄彈出一個通知。就我寫帖子時,我還沒有找到如何自定義默認(rèn)通知樣式的方法。要是有哪位大大知道怎么自定義,麻煩你告訴我下。
