整理したものはTech sheetsにあるよ

2016年6月4日土曜日

[Android]FCM(Firebase Cloud Messaging)をいれようとして大ハマりしたおはなし

みんな、こんにちはー!

Google I/O 2016で、AndroidへのPush通知の仕組みがGCMからFCM(Firebase Cloud Messaging)に移行されるという発表があったね!

そこでモケラボも、既存のアプリを1つ、GCMからFCMに移行してみたよ!

とっても簡単かなと思ってたけど、世の中そんなに甘くないね! ちゃんとPushが届くのを確認するまでに1人日(1モケラ日?)かかっちゃった。

AndroidアプリへのFCMの導入は簡単。GCMの時よりもっと簡単になってたよ!これからおはなしする問題に遭遇しなければね!

詳細はTech sheets でまとめるので、ここでは概要だけ紹介するね。

FCMを導入しよう

FirebaseのWebコンソールでプロジェクトを作る

検索するとたくさんでてくると思うので、ここは省略するね。

FirebaseのWebコンソールで、Androidアプリを登録する

アプリのパッケージ名をいれよう。SHA-1は空っぽでもだいじょうぶ
アプリを追加すると、勝手にgoogle-services.jsonのダウンロードが始まるよ。

google-services.jsonをapp配下に配置する

これは、ちょっと前のGCMと同じだね!ちなみにProductFlavorを使ってる場合は、app/src/Flavor1/google-services.json のように、app直下じゃなくてFlavorの配下に置こう。

ルートのbuild.gradleを編集する

dependenciesのところを次のようにするよ。
dependencies {
    classpath 'com.android.tools.build:gradle:2.1.0'
    classpath 'com.google.gms:google-services:3.0.0'
    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}

appのbuild.gradleを編集する

dependenciesに
compile 'com.google.firebase:firebase-messaging:9.0.1'
を追加しよう。そしてファイルの一番最後の行
apply plugin: 'com.google.gms.google-services'
を追加しよう。

これだけ!

実はこれだけでOKなんだ。初期化のプログラムを書く必要すらなくなってる!サンプルを動かすとちゃんとPushとどいたよ!

でも既存のアプリにいれたら動作しなかった!

ここからが本題。既存のアプリにいれたらなぜか動かなかったんだ。モケラボでいろいろ原因調査したので、その過程もセットでどうぞ!

トークンを取ろうとすると例外を投げる

FCMのトークンは
String token = FirebaseInstanceId.getInstance().getToken();
で取得できるよ。でもうまく動いていないときは、このメソッドが例外を投げるんだ。
java.lang.IllegalStateException: FirebaseApp with name [DEFAULT] doesn't exist.

このFirebaseAppというのがキーワードみたいだね。ということで次にFirebaseAppについて調査してみたんだ。すると。。

FirebaseAppが1つもみつからない

FirebaseAppというオブジェクトは、
List<FirebaseApp> apps = FirebaseApp.getApps(this); // thisはContext
で取得できるよ。ちゃんと動作していれば1つ以上返ってくるよ。

でも動かなかった時はこのメソッドが長さ0のリストを返していたんだ。さっきの例外はこれが原因だったのかな?

小さいプロジェクトだと1つ返ってきて、ある程度の規模のプロジェクトでなぜか1つもとれないのはなぜだろう。と、その前にこのFirebaseAppはどこで生成されてるんだろう?

Applicationクラスをこっそり差し替えたりするのかな?と思って、あえて独自のApplicationクラスをいれてみたけど、結果は同じ。ここではなさそう。

ログを眺めてみた

次に、うまくFirebaseAppが取得できた時と、失敗してる時のログをぼんやり眺めてみたよ。すると、うまくいっている時にはこんなのが見つかったよ!

06-04 07:10:09.180 9933-9933/com.mokelab.fbdemo I/FirebaseInitProvider: FirebaseApp initialization successful

「FirebaseAppの初期化に成功したよ!」というログだね。失敗してる時にはこれ無かったんだ。と、ここに新キャラの「FirebaseInitProvider」というのがいるね。Providerって名前がついてるから、きっとAndroidManifest.xmlにいるよね。

AndroidManifest.xmlを見てみた

サンプルではAndroidManifest.xmlを全く編集していないので、きっとビルド時に追加されてると思ってbuild/intermidates/manifests/の下に生成されているAndroidManifest.xmlを見てみたよ。

 <provider
   android:name="com.google.firebase.provider.FirebaseInitProvider"
   android:authorities="com.mokelab.fbdemo.firebaseinitprovider"
   android:exported="false"
   android:initOrder="100" />

やっぱりいたね!ということは、なぜかマージされないAndroidManifestが今回の原因だったみたい。マージされないのなら自分で書けばいいや!ということで、アプリ本体のAndroidManifest.xmlに次の項目を追加したらちゃんと動くようになったよ!一部アプリのパッケージ名があるのでそのままコピペして動きません!という苦情コメントは受け付けないよ!

<receiver
    android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
    android:exported="true"
    android:permission="com.google.android.c2dm.permission.SEND" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

        <category android:name="com.mokelab.fbdemo" />
    </intent-filter>
</receiver>

<!--
     Internal (not exported) receiver used by the app to start its own exported services
     without risk of being spoofed.
-->

<receiver
    android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
    android:exported="false" />

<!--
     FirebaseInstanceIdService performs security checks at runtime,
     no need for explicit permissions despite exported="true"
-->

<service
    android:name="com.google.firebase.iid.FirebaseInstanceIdService"
    android:exported="true" >
    <intent-filter android:priority="-500" >
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
    </intent-filter>
</service>

<provider
    android:name="com.google.firebase.provider.FirebaseInitProvider"
    android:authorities="com.mokelab.fbdemo.firebaseinitprovider"
    android:exported="false"
    android:initOrder="100" />

残る謎

今回解決できていないのは、どのような条件の時に、ライブラリプロジェクトのAndroidManifestの項目がマージされないかということ。だれか詳しい人、教えて!

2 件のコメント:

  1. The availability of an Uber Clone script with some ground-breaking features is still a luxury, not because of the cost, but because it isn't available on the market. To save money and get their business up and running quickly, many entrepreneurs opt for simple taxi app growth, only to be disappointed by the low customer acquisition rate.

    返信削除
  2. Users add their bank accounts, checking accounts, credit or debit cards to the Venmo clone app, which then uses them to complete requests for sending or receiving money. Users of Venmo may "request" charges from their contacts and friends (which they can add, similar to social media apps like Facebook).The person in charge may complete or reject the request once it has been submitted (but, the sender of the request is also able to "remind" the person charged of their request).Users may either hold the money in their account as their "Venmo Balance" or move it back to a bank account or credit card after the funds have been transferred.

    返信削除