情境
在早期叫做 Android Cloud to Device Messaging (C2DM),後來改成使用 Google Cloud Messaging (GCM),現在因為 Firebase 而推出了 Firebase Cloud Messaging (FCM),FCM 簡單操作而且方便,只需要幾個簡單的設定,就可以完成推播的功能。
完整程式碼
你可以到 GitHub 觀看或者下載完整程式碼。
程式碼說明
根據 如何使用 Firebase - 用 Android Studio 建立帳戶篇 所示,我們透過 Android Studio 開啟 Firebase Cloud Messaging(以下簡稱 FCM) 連結,如下圖所示。
如果你將 FCM 連結以後會看到專案內多出了一個 google-services.json
檔案,也可以看到 app 內的 build.gradle
多了一些資訊。
apply plugin: 'com.android.application'
android {
//...
}
dependencies {
//...
implementation 'com.google.firebase:firebase-messaging:20.1.0'
}
apply plugin: 'com.google.gms.google-services'
接著打開 project 內的 build.gradle 同樣多了 google service 的宣告。
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.google.gms:google-services:4.3.3'
}
}
此時我們打開 Firebase 後臺會發現多了一個 FCMDemo 的專案,如下圖所示。
點進去以後就會看到一個按鈕如下圖,點選按鈕以後就可以開始傳送我們要傳送的訊息了。
如果您要傳送訊息,會有五個步驟,第一個步驟輸入文字標題以及通知文字的對話框,如下方圖所示,我們輸入好標題跟通知文字的內容。
第二個步驟是要我們選擇要傳送的 App,有時候雙平台會共用一個 Project,或者不同用途的 App 也可以塞進同一個 Proejct 內,因此,這邊要選對 App 不然就尷尬了。
備註:一個 Firebase Project 在免費版中,只能建立 10 個 Project。
第三步驟可以選擇要傳送的時間,像我們想要立即傳送,就可以選擇現在,你也可以排定你要排定的時間傳送。
第四個步驟,你可以選擇你要轉換的事件,這邊有多個選項。
你可以選擇使用者在哪種情況接收傳送訊息,第一次開啟 App或者在 App 開啟的時候。
接下來是第五步驟,這是最後一步驟了,這邊讓你多一種傳送的方式,後面會有程式碼解說,這邊讓你用 Key : Value 對應的方式進行傳送相關資料。
當按下審查以後,就會跳出一個小視窗跟你進行最後確認。
完成以上步驟就可以透過 Firebase 後臺幫我們傳送訊息了,如果有成功跟 Firebase 串接的手機端程式就可以接收到這一則訊息,那接下來我們要撰寫的部分就是如何讓手機端接收到 Firebase 傳過來的訊息。
程式碼細節說明
我們剛剛建立好了 FCMDemo 的專案,根據官網 FCM Client Setup 的引導教學,首先,在專案內一個 Service 類別,它繼承 FirebaseMessagingService,你需要建立好兩個方法,分別為 onNewToken 以及 onMessageReceived。
- onNewToken 方法
override fun onNewToken(token: String) {
super.onNewToken(token)
Log.e("fcm", "refresh token:$token")
}
可以看到我們可以從下面這個方式來取得 Token。
FirebaseInstanceId.getInstance().getToken()
取得目前最新的 Token,這個 Token 是有時效性的,因此,當我們這個方法被呼叫的時候,就會更新到我們的 APP Server 上面 (App Server 是我們自己架設的伺服器,用來跟 FCM Server 溝通。
- onMessageReceived 方法
override fun onMessageReceived(remoteMessage: RemoteMessage){
super.onMessageReceived(remoteMessage)
}
這邊是我們需要關心的地方, 當 App Server 或者 Firebase Console 發出訊息時, 會從這個方法接收到傳送出去的訊息。
現在我們來試看看是否會收到訊息?
我們在 onMessageReceived 方法內插入了一筆 Log。
override fun onMessageReceived(remoteMessage: RemoteMessage){
super.onMessageReceived(remoteMessage)
Log.e("fcm", "onMessageReceived")
}
此時你會發現訊息都收不到, 很可能是在 AndroidManifest.xml 的 <Application></Application>
內忘記補上 Service Filter 。
<service
android:name=".MyMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
當你補上 Service Filter 以後,就會發現 Firebase 後臺送出訊息後,會有資料進來。
com.example.givemepass.fcmdemo I/message: onMessageReceived
那如果想把傳送過來的訊息印出來該怎麼辦?
可以把 Log 改成下面方式印出來。
Log.i("fcm", remoteMessage.getNotification().getBody())
當 Firebase 後臺傳送訊息以後, 就能夠在 logcat
看到訊息。
com.example.givemepass.fcmdemo I/message: 你傳送的訊息
我們在後臺傳送訊息頁面往下拉,點選進階選項,看到這樣的畫面,這邊就是前面您送出標題跟內文的地方。
後臺有另外一種通知的方式,是以 key : value 的方式傳送,通常這邊會是使用者在 App 內,我們想要讓使用者知道訊息,所進行的一種處理。我們嘗試送出去一筆資訊,將接收訊息的方法改成以下方式來處理。
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
remoteMessage.run {
var msg = StringBuffer()
//..
data.forEach {
val m = "key:${it.key}, value:${it.value}"
Log.e("fcm", "m:$it")
msg.append(m)
}
sendNotification(msg.toString())
EventBus.getDefault().post(msg.toString())
}
}
再回顧一下第五步驟,在 Firebase 後臺輸入對應的值,按下審核且送出訊息,就是在處理這部分。
看一下 Android Studio 的 logcat
,就會看到以下的訊息。
com.example.givemepass.fcmdemo I/fcm msg title:Hi, body:I want to play a game.
com.example.givemepass.fcmdemo I/fcm key:Who are you, value:I am your daddy
你會看到有兩個訊息分別來自不同的傳送方式。
- 透過 Title 以及 Body 方法來取得,這部分專門用在 Notification。
RemoteMessage.getNotification().getTitle()
RemoteMessage.getNotification().getBody()
- 透過 Key : Value 的傳送方式必須透過以下的方式來取得,再透過 foreach 的方式把資料全部撈出來,這部分用在使用者 In App 的時候。
RemoteMessage.getData()
Notification 顯示。
如果你想要做成 Notification 的方式呈現,根據官網範例只需要加入以下的方式就可以很容易實現。
private fun sendNotification(msg: String) {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(this, "")
.setSmallIcon(R.drawable.ic_android)
.setContentTitle("FCM Message")
.setContentText(msg)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(0, notificationBuilder.build())
}
只要加入這段程式碼,接著只要收到訊息以後,而且不在 App 內,就會看見 Notification 出現。
那如果說你想要接收從推播過來的訊息,剛好使用者也在 App 內,所以想把接收的結果呈現在畫面上, 應該怎麼做呢?
我們可以透過 EventBus 來達成這樣的功能,它在操作上非常簡單,只需要在要發送的地方使用以下程式碼,就會進行發送了。
EventBus.getDefault().post("你要發送的訊息")
接著在 MainActivity 定義一個方法來接收它,並且在 onCreate 以及 onPause 註冊跟反註冊即可。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
EventBus.getDefault().register(this)
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMsgEvent(msgEvent: String) {
Log.e("fcm", "msg:${msgEvent}")
msg_text.text = msgEvent
}
override fun onPause() {
super.onPause()
EventBus.getDefault().unregister(this)
}
如此一來, 就可以透過後臺傳送訊息過來的結果, 顯示在畫面上。
這樣就是一個簡單的 Firebase Notification 的範例了。