GCM 已經被 Firebase Cloud Messaging FCM 取代,以下記錄測試 FCM 的過程。
註冊 Firebase
到 firebase 的頁面登入 google 帳號,登入後點擊右上方的 "Console" 即可進入控制台,第一件事是建立Firebase 專案,進入主控台後,請按下「CREATE NEW PROJECT」建立一個新專案,專案名稱填 alarm,國家/地區 填台灣。
接下來建立一個 android 應用程式,套件名稱的部分,要跟 Android APP 一樣,我們填 tw.com.maxkit.alarm
建立 Android project
用 Android Studio 建立一個新的 project: Alarm,package name 為 tw.com.maxkit.alarm。
將剛剛新增的 firebase android 應用程式裡面下載的 "google-services.json" 這個檔案,放到 Alarm/app 這個目錄裡面。
修改以下的設定檔
- Alarm/build.gradle
增加一行 classpath 'com.google.gms:google-services:3.1.0'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
classpath 'com.google.gms:google-services:3.1.0'
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
- Alarm/app/build.gradle
增加這三個部分
compile 'com.google.firebase:firebase-messaging:10.2.6'
compile 'com.firebase:firebase-jobdispatcher:0.5.2'
apply plugin: 'com.google.gms.google-services'
完整的內容
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "tw.com.maxkit.alarm"
minSdkVersion 14
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
compile 'com.google.firebase:firebase-messaging:10.2.6'
compile 'com.firebase:firebase-jobdispatcher:0.5.2'
}
apply plugin: 'com.google.gms.google-services'
- Alarm/app/src/main/AndroidManifest.xml
增加兩個 meta-data,以及 MyFirebaseMessagingService 這個 service
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="tw.com.maxkit.alarm">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!-- 收到通知時, 到狀態列要顯示的 icon -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_stat_announcement" />
<!-- 收到通知的背景色 -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/black" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
</application>
</manifest>
這裡用到了自訂的 color 跟 icon
Alarm/app/src/main/res/values/colors.xml
要增加 black
<color name="black">#00000000</color>
- MainActivity.java
處理 notification 的部分,由系統列點擊 notification 會進入 MainActivity,在這裡取得該 notification 的資訊。
package tw.com.maxkit.alarm;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessaging;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//這一行是要註冊 alarms 這個 topic,如果不需要,就把這行刪除
FirebaseMessaging.getInstance().subscribeToTopic("alarms");
Log.d(TAG, "onCreate ..");
Intent intent = getIntent();
String msg = intent.getStringExtra("msg");
if (msg!=null)
Log.d(TAG, "msg:"+msg);
if (getIntent().getExtras() != null) {
for (String key : getIntent().getExtras().keySet()) {
Object value = getIntent().getExtras().get(key);
Log.d(TAG, "Key: " + key + " Value: " + value);
}
}
}
}
- MyFirebaseMessagingService.java
處理訊息通知的 code,主要是 onMessageReceived
package tw.com.maxkit.alarm;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends FirebaseMessagingService{
private static final String TAG = "MyFirebaseMsgService";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "onMessageReceived:"+remoteMessage+", from:"+remoteMessage.getFrom()+", data="+remoteMessage.getData());
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data notifytitle: "+ remoteMessage.getData().get("notifytitle"));
Log.d(TAG, "Message data notifybody: "+ remoteMessage.getData().get("notifybody"));
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
}
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification title: "+ remoteMessage.getData().get("title"));
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
//Display notification in notification bar
sendNotification(remoteMessage.getData().get("notifytitle"), remoteMessage.getData().get("notifybody"));
}
private void sendNotification(String notifytitle, String notifybody) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri=
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_access_alarms)
.setContentTitle( notifytitle )
.setContentText( notifybody )
//.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
}
用 Firebase 網頁測試
在左側點擊 Notifications 的部分,填寫以下的訊息,就會以 notification 的方式,收到訊息。意思就是說,當 APP 在背景時,會直接在系統列裡面收到 notification。
用 curl 測試
根據 Firebase Cloud Messaging HTTP Protocol 的說明,可以直接用 http 的方式發送訊息。
在專案設定 -> Cloud Messaging 的地方,可以找到 Authorization Key 伺服器金鑰
以下是 curl 的測試,"Authorization: key=" 後面要換成剛剛取得的 伺服器金鑰。
data 的部分可以參考 Firebase Cloud Messaging HTTP Protocol 的說明,以下我們設定了 to alarms 這個 topic,另外只傳送了 data 區塊,在 android APP 不管是前景或背景,都會在 onMessageReceived 收到訊息,我們可在那邊發送 local notification 把資料顯示在系統列上。
curl -X POST -H "Content-Type: application/json" \
-H "Authorization: key=yourkey" \
https://fcm.googleapis.com/fcm/send \
-d '{"to":"/topics/alarms", "priority":"high", "data":{"notifytitle":"測試title", "notifybody":"測試body", "訊息 key": "訊息 Topic!", "key2": "value2"}}'
也可以加上 notification 的區塊,這部分就會跟在 Firebase 頁面測試的結果一樣,Android APP 會直接收到 notification。
curl -X POST -H "Content-Type: application/json" \
-H "Authorization: key= yourkey" \
https://fcm.googleapis.com/fcm/send \
-d '{"to":"/topics/alarms", "priority":"high", "notification":{"title" : "title","icon" : "new","body" : "訊息 body"}, "data":{"訊息 key": "訊息 Topic!", "key2": "value2"}}'
References
在Android中使用2016新版Firebase加快開發過程(一)
Firebase雲端訊息-發送測試通知至Android APP中
Firebase Cloud Messaging (FCM) 入門到進階應用(1) --- 開發環境建立
Android: 利用Firebase實作使用者間的產品分享機制
Send Messages to Multiple Devices
Firebase push Notification using curl command — Devoid Backend
for ios
Push Notification教學:如何使用Firebase在iOS實現推播功能