歴史的経緯?GCMで送信したデータが文字列化される件について。

GCMで少し変わった挙動を観測しましたのでご報告。

Growth PushのGCMの送信データの取り扱い

Growth Pushでは、GCMの送信データを、たとえば次のような形式で送信することにしました。

{"message":"message","sound":true}

しかし、このsoundのtrueが、正しく扱えておらず調査をしていました。

GCMへの送信データはJSONで型の制約はない。

GCMのリクエストボディはJSONなので、文字列、数値、bool値は厳密に区別されているのかと考えていました。
たとえば、次のようなリクエストをGCMに対して送ります。

{"data":{"message":"お知らせがあります!","growthpush":{"notificationId":1234},"badge":1,"sound":true},"dry_run":false,"registration_ids":["APA91bHxxxxxxxxxxxxxxxxxxx"]}

これを受信するクライアントは、dataキー内のオブジェクトを、Bundleオブジェクトとして受け取ることができます。
このBundleオブジェクトは、直感的には次のような構造になっていると考えます。

{
	message: 'お知らせがあります!',
	growthpush: {
		notificationId: 1234
	},
	badge: 1
	sound: true
}

・・・が、違いました。

GCMから受信されるデータは全て文字列扱い

ドキュメントを読み、ちゃんと検証してみると、GCM経由で受信されたデータは全て文字列に丸められることを知りました。
現実には、次のような形です。

{
	message: 'お知らせがあります!',
	growthpush: '{"notificationId":1234}',
	badge: '1',
	sound: 'true'
}

オブジェクトの部分は、Bundleで扱っている以上、構造を維持できないのは仕方ないのかもしれません。しかし、BundleクラスはgetIntやgetBooleanというメソッドもあるにも関わらず、integerやbooleanも文字列に丸められてしまいます。
この件はしっかりドキュメントにも記述されています。

The values could be any JSON object, but we recommend using strings, since the values will be converted to strings in the GCM server anyway.

スクリーンショット 2013-11-05 23.13.53
Google Cloud Messaging for Android | Android Developers

bool値を送信してもBundle#getBooleanで受け取れない

注意すべきは、soundキーに対して送ったbool値を、BundleのgetBooleanメソッドで受け取ろうと思っても受け取ることができないということです。

intent.getExtras().getBoolean("sound")

次のような感じで警告が出ます。

11-01 12:54:17.724: W/Bundle(7432): Key sound expected Boolean but value was a java.lang.String.  The default value false was returned.
11-01 12:54:17.724: W/Bundle(7432): Attempt to cast generated internal exception:
11-01 12:54:17.724: W/Bundle(7432): java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean
11-01 12:54:17.724: W/Bundle(7432): 	at android.os.Bundle.getBoolean(Bundle.java:809)
11-01 12:54:17.724: W/Bundle(7432): 	at android.os.Bundle.getBoolean(Bundle.java:769)
... (略) ...

実際には、bool値ではなく文字列として受け取ってしまっているので、文字列をパースさせる必要があります。

Boolean.valueOf(intent.getExtras().getString("sound", "false"))

その他整数値なども同様です。
なぜJSONで型を意識しながら送っているのに文字列化されてしまうのかなと考えてみると、C2DM時代からの歴史的経緯なのかなと思います。GCMのリクエストボディは、application/jsonですが、C2DMのリクエスト形式は、確かapplication/x-www-form-urlencodedで、これは値を文字列として扱う他ないためです。

Growth PushのAndroid SDKにも反映しておきました。

この件につきましては、Growth PushのAndroid SDKにも修正を反映させておきました。
Fix handling “sound” parameter · eab5e8d · SIROK/growthpush-android
他にも、Android SDKを拡張性高く使えるような設計の変更を入れました。
AndroidのGCMの取り扱いはゼロから作ると結構面倒くさいのですが、Growth Pushを使うと簡単なので、まだの方はぜひ一度登録してダッシュボードを見てみてください!
 a
Growth Push
細かい修正しながら安定的かつ高速に動作するように改善を繰り返しています。

タイトルとURLをコピーしました