Android Push通知 GCMからFCMに移行 #Android #Firebase

#FCMへの移行について
「FCM（Firebase Cloud Messaging）はGCMの最新バージョンです。GCMの機能を含め新機能を実装しています。新しくPush通知を実装する場合はFCMを使用し、現在GCMを使用している方はFCMに移行することを強くオススメします。」とGoogleは公言[^1]しているので、GCMからFCMに移行しましょう。

##Firebaseの準備
FCMの準備をします。まずはFirebaseの登録をしていない方は[こちら](https://firebase.google.com/docs/android/setup)を参考に登録してください。

- FirebaseにAndroidプロジェクトを登録
- ```google-services.json```をAndroidプロジェクトに入れる
- Firebaseのコンソールにアクセス
- セッティングのマーク（歯車）をクリック、Project Settingに入る
- そこにある```Server Key``` と ```Server ID```をサーバー管理者に伝える

##サーバーのEndpoint変更

・GCM endpoint
gcm-http.googleapis.com/gcm/
gcm-xmpp.googleapis.com

下記に変更

・FCM endpoint
fcm.googleapis.com/fcm/
fcm-xmpp.googleapis.com

これでFirebase側とサーバー側の設定は完了です。次はAndroidプロジェクト側を見ていきます。

##クライアントからサーバーへの初期設定を実装

###必要ライブラリをインストール

```text:build.gradle
dependencies {
    ...
    compile 'com.google.firebase:firebase-messaging:10.0.1'
}
```

こちらは削除
```compile "com.google.android.gms:play-services-gcm:8.4.0"```



##AndroidManifestの設定
下記のPermissionを削除

```xml:AndroidManifest.xml
<uses-permission android:name="android.permission.WAKE_LOCK" />
<permission android:name="<your-package-name>.permission.C2D_MESSAGE"
            android:protectionLevel="signature" />
<uses-permission android:name="<your-package-name>.permission.C2D_MESSAGE" />
```

Receiverを削除

```xml:AndroidManifest.xml
<receiver
    android:name="com.google.android.gms.gcm.GcmReceiver"
    android:exported="true"
    android:permission="com.google.android.c2dm.permission.SEND" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <category android:name="com.example.gcm" />
    </intent-filter>
</receiver>
```

Serviceクラスを以下の様に書きなおす

以前の実装

```xml:AndroidManifest.xml
<service
    android:name=".MyInstanceIDListenerService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.android.gms.iid.InstanceID" />
    </intent-filter>
</service>
```

新しい実装

```xml:AndroidManifest.xml
<service
    android:name=".MyInstanceIDListenerService">
    <intent-filter>
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
    </intent-filter>
</service>
```

##Serviceクラスの実装

```java:MyInstanceIDListenerService.java
public class MyInstanceIDListenerService extends InstanceIDListenerService {
  ...
  @Override
  public void onTokenRefresh() {
      Intent intent = new Intent(this, RegistrationIntentService.class);
      startService(intent);
  }
}
```

このように実装していたものを下記のようにFCM用の実装に変更します。

```java:MyInstanceIDListenerService.java

public class MyInstanceIDListenerService extends FirebaseInstanceIdService {
  ...
  @Override
  public void onTokenRefresh() {
      String refreshedToken = FirebaseInstanceId.getInstance().getToken();
      Log.d(TAG, "Refreshed token: " + refreshedToken);
      //サーバーにrefreshedTokenを送信し初期設定を行う
　　　 //※任意メソッド
      sendRegistrationToServer(refreshedToken);
  }
}
```

下記のような実装は必要ないので削除

```java:MyInstanceIDListenerService.java
 InstanceID instanceID = InstanceID.getInstance(this);
 String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
          GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
 Log.i(TAG, "GCM Registration Token: " + token);
```

Registration tokenを取得する方法がシンプルになりましたね。ここで取得したRegistration tokenを任意のサーバーに送信すれば初期設定完了です。

##Push通知の受信を実装

###AndroidManifestの設定

下記の設定を変更

以前の実装

```xml:AndroidManifest.xml
<service
    android:name=".MyGcmListenerService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
    </intent-filter>
</service>
```

新しい実装

```xml:AndroidManifest.xml
<service
    android:name=".MyFcmListenerService">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>
```

###GcmListenerServiceをFirebaseMessagingServiceに移行

```java:MyGcmListenerService.java
public class MyGcmListenerService extends GcmListenerService {
  @Override
  public void onMessageReceived(String from, Bundle data){
    ...
  }

  ...
}
```

```java:MyFcmListenerService.java
public class MyFcmListenerService extends FirebaseMessagingService {
  @Override
  public void onMessageReceived(RemoteMessage message){
    String from = message.getFrom();
    Map data = message.getData();
  }
  ...
}
```

これでPush通知を受け取る事ができます。
受け取ったら下記のようにNotifyを出してみましょう。

```java:MyFcmListenerService.java

@Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        String msgBody = null;

        // TODO(developer): Handle FCM messages here.
        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());
            msgBody = remoteMessage.getNotification().getBody();
        }

        if (msgBody != null){
            sendNotification(msgBody);
        }
    }


private void sendNotification(String messageBody) {
        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.mipmap.ic_launcher)
                .setContentTitle("FCM Message")
                .setContentText(messageBody)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
    }
```


##おまけ
GcmPubSubの使用状況を更新する

```java:GCM
GcmPubSub.getInstance(context).subscribe("token", "/topics/mytopic", null /* extras bundle */);
GcmPubSub.getInstance(context).unsubscribe("token", "/topics/mytopic");
```

下記に変更

```java:FCM
FirebaseMessaging.getInstance().subscribeToTopic("mytopic");
FirebaseMessaging.getInstance().unsubscribeToTopic("mytopic");
```

##参考にしたサイト
[^1]:[Migrate a GCM Client App for Android to Firebase Cloud Messaging](https://developers.google.com/cloud-messaging/android/android-migrate-fcm)