そごうソフトウェア研究所

SOA、開発プロセス、ITアーキテクチャなどについて書いています。Twitterやってます@rsogo

"Google Cloud Messaging for Android"まとめ

Google I/O 2012にGoogle Cloud Messaging for Androidというセッションがあり、youtubeでも公開されています。


Google I/O 2012 - Google Cloud Messaging for Android

以下は、セッションの完全日本語訳ではなくて、私の感じた重要そうな点をピックアップしています。


Google Cloud Messaging(以降、GCM)はCloud To Messaging Device(C2MD)の後継です。GCMはバックエンドのサービスが、Androidアプリに対してメッセージを送るときの仕組みをクラウドサービスとして提供しています。


バックエンドのサービスはGCMにメッセージを送れば、GCMが各Androidデバイスのアプリにメッセージを届けてくれます。この仕組みは、バックエンドのサービスが情報の更新をAndroidデバイスに通知するような場合に適しています。各デバイスからバックエンドにポーリングするのは面倒だし、無駄な通信やバッテリーを使うことになりますからね。


最初、アプリはGCMに対して登録依頼を行うとGCMからレジストIDをもらえます。このレジストIDをバックエンドのサービスに知らせることで、アプリはバックエンドのサービスからメッセージを受け取れる状態になります。


アプリ側でバックエンドからGCM経由で送信されたメッセージを受け取るには、GCMBaseIntentServiceを実装したクラスでonMessageメソッドをオーバーライドします。


GCMはマルチキャストをサポートしていて、バックエンドのサービスは複数のデバイスのレジストIDを指定して一度のGCMへの通信で、複数のデバイスへメッセージを配信することができます。
私も気になったのですが、最後のQ&Aで質問があったとおり、ブロードキャスト、つまりレジストIDを指定せずに全部の端末にメッセージを送信することはサポートされていません
バックエンドの実装として、大量のデバイスにメッセージを送信する必要があった場合には、複数回に分けて配信するようなロジックにしておく必要がありそうです。


また、メッセージのサイズは4キロバイトが上限で、基本的には簡単なメッセージのやり取りか、イベントだけ通知して大きなデータはアプリからバックエンドのサービスに直接取りに来るような仕組みが推奨されています。


Googleの世界でのサービスなので、デバイス側に用意されるGCMフレームワークAndroidにしか対応していません。そのため、iOS版、Android版両方のアプリを提供しているアプリ提供者は、それぞれ別の仕組みを使うか、AppleGoogle以外のメッセージングのサービスを検討しないといけないですね・・・・。


参考情報
Googleが提供するGCMのドキュメントはここから行ける。Getting Startや、Architectureのページが含まれます。
http://developer.android.com/guide/google/gcm/index.html

Google API Console
https://code.google.com/apis/console/b/0/
プロジェクトを作ったり、GCMを有効にしたり、APIキーを取得するのはここからです。

バックエンド側でGCMとやり取りするためのライブラリが提供されています。以下、JavaDoc
http://developer.android.com/guide/google/gcm/server-javadoc/index.html

2012年8月版 Android Platformの統計情報。どんなAndroidデバイスが使われているか

Androidアプリの開発における特徴の一つとして、Androidを搭載しているデバイスの多様さと、iOSと比較して古いバージョンのプラットフォームが使われ続けているというものがあります。
古いバージョンが使われ続ける理由として、Googleが提供しているAndroidのモジュールに対して各デバイスメーカーがカスタマイズしているため、GoogleSDKのバージョンアップに対してデバイスメーカーが対応できないという背景があります。


そのため、我々、アプリの提供者からすると、どのようなプラットフォームを狙って開発を行うかというのが、予算とシェアのバランスになります。そんなとき参考になる情報がいくつか公開されています。


まず、Androidの公式Developerサイトで世界中でどんなAndroidのプラットフォームが使われているか、統計情報が公開されています。

http://developer.android.com/about/dashboards/index.html


2012年8月20日時点で、公開されている情報は、、、
・2012年8月1日を最終日とする14日間にGoogle Playにアクセスしてきた端末のAndroidのバージョンのシェア
・同じくAndroidのバージョンのシェアが時間と共に遷移するグラフ

2.3系がまだまだ半数以上を占めていますが、4.xも急激にシェアを伸ばしている(15.8%)ことが分かります。

こちらの記事が2012年1月のものですから、2012年に入って2.2のシェアはかなり下がってきており、こらから新規に開発するものや、デバイスの種類をある程度コントロールできる企業内で使用するものなど、場合によってはサポート対象から外すということも選択肢に入れても良いでしょう。

他にも画面のサイズ、Open GLのバージョンのシェアも公開されています。


また、こちらのサイトでは、Android SDKのシェアも同じく公開されていますが、有償アプリと、無償アプリのダウンロード数の比較などが公開されています。Androidアプリのダウンロード数の多さ分類されたグラフを見ると、やっぱりヒットしているアプリの割合は少なく、競争が激しいことも分かります。

http://www.appbrain.com/stats/

Android SQLiteデータベースのテーブル変更はこうやる

データベースのテーブルのカラム追加や、定義の変更っていうのはAndoroidアプリでも大変なことですよねぇ。

android.database.sqlite.SQLiteOpenHelperにデータベースのバージョンを上げたときに呼び出されるメソッド、onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)が用意されているので、SQLiteOpenHelperを継承して実装します。


このメソッドはアプリが起動して最初にデータベースを使う時に、データベースのバージョンが変わっていれば呼び出されます。
具体的にはContentProviderが初期化(onCreateメソッド)される時にSQLiteOpenHelperを継承したクラスをインスタンス化しますが、その時にDBのバージョンを指定します。

	@Override
	public boolean onCreate() {
		
		SQLiteOpenHelper dbHeler =new DBHelper(
				getContext(),
				DB_NAME,
				null,
				DB_VERSION,
				new TaskTable());
		
		db = dbHeler.getWritableDatabase();
		return (db != null);
	}

このときに、バージョンアップ前のバージョンと、アプリをバージョンアップした時に指定してきたDBのバージョン(上のコードではDB_VERSIONがバージョンの定数定義)が違うことが分かるのです。逆に言えばアプリ開発者はこのDBバージョンを上手く使い分けてあげる必要があります。


onUpgradeメソッドのoldVersionには今アプリ内でデータを保存しているデータベースのバージョン、newVersionには新しくバージョンアップしたアプリのデータベースのバージョンが入ります。

今アプリ内に持っているデータが必要無い場合は、今あるテーブルを削除して、もう一回作り直せばOK。


でも、一度リリースしてしまえば、データが必要無い場合なんてないですよね。一番多いケースのカラム追加であれば、次のようにalter tableでカラム追加。もともと入っているデータ用にデフォルト値をしてしてあげればOK。

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// DBバージョンアップ時のデータ移行を実装
		if (oldVersion < newVersion) {
			if (oldVersion == 3) {
				
				db.execSQL(
						"alter table " + TaskTable.DB_TABLE_NAME +
						" add isUploaded boolean default 'false'
						);
	
			}
			if (oldVersion == 3 || oldVersion == 4) {
				
				db.execSQL(
						"alter table " + TaskTable.DB_TABLE_NAME +
						" add idOfServer text"
						);
			}
		}
	}


テーブルの定義ががらっと変わってしまうような大きな変更の場合は、一旦Selectしてテーブル作成後に新しい定義でInsertするようなデータ移行を考えないと行けないですね。


なんにしてもデータベースの変更は慎重に。

複数のコンテンツプロバイダーの登録

複数のコンテンツプロバイダーのauthoritiesを1つのContentProviderクラスを継承した自分のコンテンツプロバイダーにマッピングすることができます。AndroidManifest.xmlの書き方はこのようになります。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >

略
        <provider android:authorities="com.sample.task" android:name="com.sample.DataProvider"></provider>
        <provider android:authorities="com.sample.project" android:name="com.sample.DataProvider"></provider>
        <provider android:authorities="com.sample.comment" android:name="com.sample.DataProvider"></provider>
    </application>
</manifest>

何がうれしいかと言うと、ここではcom.sample.DataProviderにはテーブルや依存したコードはもっておらず、外から指定するコンテントプロバイダーのURI(content://com.sample.task/など)に応じて処理を切り替えるような書き方ができます。

Androidアプリケーションからのデータベースの利用(概要)

AndroidからのDatabaseの利用を理解するには、ContentProvider、SQLiteDatabaseクラスSQLiteOpenHelperクラスが分かっていればとりあえずOKです。


JavaSQLになじみがあるエンジニアであれば、理解し易いと思います。


SQLiteOpenHelperは抽象クラスなので継承したクラスを作成して、以下のメソッドをオーバライドします。
public void onCreate(SQLiteDatabase db)
データベースファイルが無いときに呼び出されます。SQLiteDatabaseクラスとSQLを使って、テーブルを作成するなど、データベースを使うための準備をします。

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
SQLiteOpenHelperクラスのコンストラクタの引数に、バージョンがありますが、このバージョンが上がったときに呼ばれます。テーブルのカラム追加や、テーブルの追加など、データベースの仕様が変わったときに、、、

古い仕様のテーブルを削除してからonCreateメソッドを呼び出して、新しいテーブルを作成する
古い仕様のデータベースから、新しい仕様のデータベースにデータを移行する

というような処理をします。

Facebookと連携するAndroidアプリのEclipseプロジェクト

普通のAndroidプロジェクトとしてプロジェクトを作成しますが、一点、追加で設定します。

事前にダウンロードしたFacebook SDKを元にEclipseプロジェクトを作成します。手順はマニュアルを見てみて下さい。
https://developers.facebook.com/docs/mobile/android/build/#sample

この後に、自分のアプリのEclipseプロジェクトを作っていきます。


マニュアルの通りで上手くいかなかったのが、自分のアプリのプロジェクトからFacebook SDKを参照する設定。
マニュアルだと、上で作ったプロジェクトを参照すればOKの様なのですが、Facebookクラスが見つからないとエラーになるので、必要なクラスをjarにアーカイブして、自分のプロジェクトのlibに組み込みます。
僕の設定だとcom_facebook_android.jarというファイルを入れてます。
f:id:begirama:20120515061427j:image



ビルドパスに含めて、、、
f:id:begirama:20120515061428j:image



読み込む順番もTOPにしないとエラーになりました。
f:id:begirama:20120515061426j:image

自動で作られるjarはR系のクラス等、いくつか含まれていなかったので、自分で追加するようにしました。



認証の時にネットワークアクセスをするため、AndroidManifest.xmlを編集して、パーミッションandroid.permission.INTERNET」を追加。


package="test.facebook"
android:versionCode="1"
android:versionName="1.0" >

android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
android:name=".FacebookOAuthSampleActivity"
android:label="@string/app_name" >






アプリのKey Hashをfacebookに登録

Javaで鍵や証明書を管理するkeytoolというツールと、上でセットアップしたOpenSSLを使ってKey Hashを作成します。

1.Javaのバージョン確認
>java -version
java version "1.6.0_18"
Java(TM) SE Runtime Environment (build 1.6.0_18-b07)
Java HotSpot(TM) Client VM (build 16.0-b13, mixed mode)



2.証明書をエクスポート
>keytool -exportcert -alias androiddebugkey -keystore .android\debug.keystore > cert.txt

キーストアの場所はデフォルトではここにあります。
\.android\debug.keystore
キーストアのパスワード'android'です。
これはデバッグ用なのかな?本番はどうするのだろうか。



3.証明書ファイルをバイナリ形式で読み込んでSHA1でエンコード
>openssl sha1 -binary cert.txt > sha1.txt



4.SHA1エンコードされたデータをbase64形式でテキストに変換
>openssl base64 -in sha1.txt
テキストが出力されます。

このテキストをFacebook上のアプリの管理画面でAndroid Key Hashに設定します。
f:id:begirama:20120515195241j:image:w640