Edited at

【Android】GCMからFCM(Firebase Notification)に移行したときの情報まとめ

More than 3 years have passed since last update.

スクリーンショット 2016-06-16 0.11.58.png

Google I/Oで発表されてから盛り上がっているFirebase

GCMからFirebaseに乗り換える方向けに移行方法、テスト方法をまとめたものです。

https://firebase.google.com/


FCMはすごく簡単

gcm.jarの頃


  • Registration IDが空になったら再発行処理が必要だった

play-services-gcmに移行したての頃


  • gcm.jarでは内部でやってくれていた、アプリバージョンをチェックして再発行などの再登録処理を自前で用意

FCM


  • SDK内ですべてやってくれる


移行メリット


  • 移行コストが低い。シンプル

  • 基本的にサーバーサイド変更不要

  • デバイス登録処理やトークン発行処理は自動でやってくれる

  • 今後のプッシュ関連の機能追加はFCMで行われる

  • サーバーレスも可能

  • Firebase Analyticsと連携した配信も可能

  • GCMはいつか止まる


移行方法


gcm.jarを使ってる場合

とりあえず、gcm.jarを消して↓を見てください

Github firebase/quickstart-android

そのあとに、ここを見ると良いです。

Set Up a Firebase Cloud Messaging Client App on Android


play-services-gcmを使ってる場合

基本的にはこの通りやるだけ

Migrate a GCM Client App for Android to Firebase Cloud Messaging


  • AndroidManifest.xmlはたったの2つのServiceを追加するだけ

  • Permissionはなんと不要!(※ライブラリ側のAndroidManifest.xmlに記述されているため)

  • 移行するとコード減ります。


確認方法など


トークン発行タイミング


  1. 初回起動時に自動でFCM SDKがregistration tokenを発行してくれる。

  2. tokenが変わるたびにFirebaseInstanceIdServiceonTokenRefresh()で最新のtokenを取得できるのでサーバーに登録したりする


MyFirebaseInstanceIDService.java

@Override

public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);

// TODO: Implement this method to send any registration to your app's servers.
sendRegistrationToServer(refreshedToken);
}



InstanceIDが変わった時のテスト

InstanceIDが変わる条件はいくつかあるようです。


  • アプリ内でInstanceIDを削除した時

  • 新しいデバイスで復元された時

  • アンインストール/再インストール

  • アプリデータを削除


adb

adb shell am startservice -a com.google.android.gms.iid.InstanceID --es "CMD" "RST" -n your.package.name/your.own.MyInstanceIDListenerService


↑の方法でもonTokenRefresh()が呼ばれなかったので、DeleteTokenServiceを仕込んで呼ぶことにしてみます。


startservice

$ adb shell am startservice your.packeage.name/your.own.service.DeleteTokenService


すると、onTokenRefresh()が呼ばれることが確認できました。

DeleteTokenServiceは内部でFirebaseInstanceId.getInstance().deleteInstanceId();を呼んでるため、過去に発行されたregistrationIdも無効にります。

Documen deleteInstanceID()


ユーザーへの影響の確認

GCMからFCMに移行できたけど、アップデートの検証もしたい所。


  • 現在ストアに上がってるGCM版のAPKを用意

  • FCMに移行したバージョンのAPKを用意


手順

// 事前にonTokenRefresh()の中でログを仕込んでおく(今回はtokenというタグ)

$ adb logcat | grep token

// GCM版をインストールしたら一度起動する
$ adb install gcm.apk

// 新しいバージョンを入れて、onTokenRefresh()内に仕込んだtokenログが出力されていることを確認する
$ adb install -r fcm.apk


サーバーに新しいデバイストークンを送ってる場合、トークンが書き換わってるかなども確認しておきましょう。


注意


Firebaseプロジェクト作成時

スクリーンショット 2016-06-16 0.11.14.png

すでにGoogleプロジェクトがある場合、Firebaseで新規プロジェクトを作らないこと。

apiキーが違うのでいろいろトラブルが起きます。

Googleプロジェクトをインポートから追加してください。

(AmazonSNSを利用しているプロダクトで、すでにGoogleプロジェクトと連携させている状態で、Firebase Analyticsを新規で導入したところ、APIキーが合わずプッシュが来なくなる問題が発生しました。)


FirebaseMessagingService内でHandlerを使う時

gcm.jarの頃、GCMBaseIntentServiceIntentServiceを継承していたため、new Handler()が使えました。

今回はFirebaseMessagingServiceServiceを継承しているため、そのままnew Handler()をしようとするとRuntimeExceptionが発生します。

java.lang.RuntimeException

Can't create handler inside thread that has not called Looper.prepare()


looperを作りましょう。

if (Looper.myLooper() == null) {

Looper.prepare();
}


エラーがでたら


java.lang.IllegalStateException: FirebaseApp with name [DEFAULT] doesn't exist.


build.gradle

// これが追加されてるか確認

apply plugin: 'com.google.gms.google-services'


期待したいこと

実装方法がだいぶ簡単になったので、しばらくは大きく変わることは無いでしょう。(希望)

これからはプッシュ配信運用で便利な機能追加を期待しています。

定期配信ができるとか。

(あと複数Token発行してしまっても、自動で古い方をオフに出来るとか...)


参考

When will InstanceIDListenerService be called and how to test it?

FCM Vs GCM? Why we need to migrate from GCM to FCM

Firebase FCM force onTokenRefresh() to be called

FirebaseInstanceIdService

FirebaseApp with name [DEFAULT] doesn't exist

Firebase FCM force onTokenRefresh() to be called


DeleteTokenService


http://gohowall.com/firebase-fcm-force-ontokenrefresh-to-be-called/



DeleteTokenService.java

public class DeleteTokenService extends IntentService

{
public static final String TAG = DeleteTokenService.class.getSimpleName();

public DeleteTokenService()
{
super(TAG);
}

@Override
protected void onHandleIntent(Intent intent)
{
try
{
// Check for current token
String originalToken = getTokenFromPrefs();
Log.d(TAG, "Token before deletion: " + originalToken);

// Resets Instance ID and revokes all tokens.
FirebaseInstanceId.getInstance().deleteInstanceId();

// Clear current saved token
saveTokenToPrefs("");

// Check for success of empty token
String tokenCheck = getTokenFromPrefs();
Log.d(TAG, "Token deleted. Proof: " + tokenCheck);

// Now manually call onTokenRefresh()
Log.d(TAG, "Getting new token");
FirebaseInstanceId.getInstance().getToken();
}
catch (IOException e)
{
e.printStackTrace();
}
}

private void saveTokenToPrefs(String _token)
{
// Access Shared Preferences
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = preferences.edit();

// Save to SharedPreferences
editor.putString("registration_id", _token);
editor.apply();
}

private String getTokenFromPrefs()
{
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
return preferences.getString("registration_id", null);
}
}



メモ

これを書くのに2時間も掛かった。。。

段落の使い方が理解できてない

もっと質の良いアウトプットをしていこう。