egawa_20090711

GAE/Jとつなげてみたり
江川 崇
さいしょに
「Google Android プログラミング入門」
でましたー
648ページ
3,800円(税別)
アスキーメディアワークス
「Androidあぷぷ」より抜粋
•
狩猟系と農耕系のどっちって言われたら、これは狩猟系・・・と見せかけて農耕系かも。
・・・
実際どうよ?
・・・
•
•
•
ここ数ヶ月でAndroid関連の書籍も充実してきており、他の書籍で扱っているトピックか大体
被ってしまっている状況が続いていますが、この本はページ数が多いこともあり他の書籍で
扱ってないトピックが多いです。特に第4部以降で顕著であり、日本以外でも自作アプリを
使ってもらうためのリソースと国際化、最近のJava開発では必須になりつつあるテスティング
フレームワーク、日本語変換など入力値を推測・加工することで入力補助を行うInput
Method Framework、最近NDKが出たことで一気に注目されつつあるJNI(Java Native
Interface)などは、Webを含めてもきちんとまとまっている情報が少ないので、大変助かりま
す。
各トピックの始まり、または言葉では説明しにくい動作を扱う場合など、全体を通してフロー
チャートや状態遷移図などが随所にちりばめられており、すごく理解しやすいです。特に
Androidの場合、ActivityやIntentの扱いが独特なのですが、これがイメージとしてスッと理解
しやすくなっているのは大きいです。
何気にParcelable(JavaのSerializableのようなもの)の解説があるのも助かります。
• (個人的には)将来青本と呼ばれるぐ
らい定番になる気がしています。
将来青本と呼ば
れるぐらい定番に
なる気がしていま
す。
きっかけ
• 端末一台でもいろいろできるが、端末間がつ
ながった方が面白い
– 端末同士が直接やり取りするのはムズい、なんら
かのサーバーを介する方法が楽
– Android Hackathonで、GAE(Google App Engine)
を使ってたグループがあった
簡単そうだし、ちょっと試してみようか
GAEは何が嬉しそう?
• とりあえず無料でURLを得られる
– http://アプリ名.appspot.com/
– 固定のURLは色んなAPIの口として使える
– Waveとの連携とか
• デプロイメントが楽
• MTBFが長そうなGoogleインフラ
– 有料サービスもしてる
• Javaも使える(GAE/J)
– 有料でもJavaが使えるホスティングはなかなか無いよね?
くらいかなあ・・・?
GAEの詳しそうな資料発見!
スティルハウス佐藤さん
http://tinyurl.com/kaz279
Simpleなチャット
なんちゃってlatitude
通信(1)
• とうぜんアンドロからHTTPを投げる(簡単)
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(url);
// new HttpPost(uri);
request.setHeader("User-Agent", userAgent);
try {
HttpResponse response = client.execute(request);
StatusLine status = response.getStatusLine();
if (status.getStatusCode() != HTTP_STATUS_OK) {
// えらー
}
HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent();
・・・
} catch (IOException e) {
// えらー
}
通信(2)
• UIスレッドが止まらないよう別スレッドでHTTP
– 戻ってきたらHandlerでUIスレッドに紛れ込ませる
final Handler handler = new Handler();
final Runnable uiRunnable = new Runnable() {
@Override
public void run() {
・・・UI部品の書き換えとか
}
};
final Thread requestThread = new Thread() {
@Override
public void run() {
・・・HTTPの要求、応答の処理
handler.post(uiRunnable);
}
};
・・・
requestThread.start();
通信(3)
• 応答がJSONなら (簡単)
String responseBody = ・・・・InputStreamからStringに変えて・・・・
JSONObject response = new JSONObject(responseBody);
JSONObject value1 = response.getJSONObject("key1");
・・・・
正直これだけでできちゃうんだけど、、、、
違う方法も試してみよう
Protocol Buffers
• オブジェクトをマーシャル、アンマーシャルする仕
組 + RPCをするラッパー(Service)
http://code.google.com/p/protobuf/
• 通常のシリアライズとどう違う?
– サイズが小さくなるらしい
– 後から項目を拡張しても大丈夫らしい
– 複数の言語で使えるらしい(Java、C++、Python)
• 詳しくは、白石さんの記事をご覧下さい
http://journal.mycom.co.jp/articles/2008/07/18/protocolbuffer/
Protocol Buffers
独自のデータ項目定義
(protoファイル)
message Person {
required int32 id = 1;
required string name = 2;
optional int32 age = 3;
}
・・・
protoツールで
生成
Javaのソース
Pythonのソース
Cppのソース
$ protoc --java_out=src --cpp_out=src --python_out=src ./Persons.proto
試す
• オブジェクトをSDカードに出力
Person.Builder builder = Person.newBuilder();
Person p = builder.setName("えがわ").setId(1).build();
OutputStream out = new FileOutputStream("/sdcard/hoge.txt");
p.writeTo(out);
out.close();
• SDカードから復元
InputStream in = new FileInputStream("/sdcard/hoge.txt");
Person p = Person.parseFrom(in);
in.close();
HTTPにのせる
• サービスをprotoファイルに記述
・・・
service MyRpcService {
rpc doSomething(Request) returns (Response);
}
・・・・
– MyRpcServiceにはdoSomethingというメソッドがある
– doSomethingメソッドのパラメータの型はRequestクラス、
戻り値の型はResponseクラス
というかんじの定義になる
サービス呼び出し部分
• こんなかんじ
HttpRpcChannel channel = new HttRpcChannel(uri);
RpcController controller = ※ 何か指定する
MyRpcService service = MyRpcService.newStub(channel);
service.doSomething(controller, request, new
RpcCallback<Response>() {
public void run(Response response) {
・・・応答を受け取ったときの処理
}
});
HttpRpcChannel側
• こんなかんじ
public class HttpRpcChannel implements RpcChannel {
private final URI uri;
public HttpRpcChannel(URI uri) {
this.uri = uri;
}
private ResponseHandler<Response> handler = new ResponseHandler<Response>() {
@Override
public Response handleResponse(HttpResponse response)
throws ClientProtocolException, IOException {
HttpEntity entity = response.getEntity();
int length
= (int) entity.getContentLength();
byte[] buffer = new byte[length];
entity.getContent().read(buffer, 0, length);
return Response.parseFrom(buffer);
}
};
・・・・・次のページ
HttpRpcChannelの続き
・・・前のページから
public void callMethod(MethodDescriptor method, RpcController controller,
Message request, Message prototype, RpcCallback<Message> done) {
try {
・・・ほんとはメソッド名で分岐
HttpPost post = new HttpPost(uri);
HttpClient client = new DefaultHttpClient();
byte[] data = request.toByteArray();
HttpEntity entity = new InputStreamEntity(new ByteArrayInputStream(data),
data.length);
post.setEntity(entity);
Response response = client.execute(post, handler);
done.run(response );
} catch (Exception e) {
// エラー処理
}
}
}
• こんなかんじ
サーバー側は?
• サーブレットならこんな感じ(簡単)
public class MyServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Request request = Request.parseFrom(req.getInputStream());
いれもの = request.げっとほげほげ
・・・
Builder builder = Response.newBuilder();
builder.せっとほげほげ
・・・
builder.build().writeTo(resp.getOutputStream());
}
}
GAE/Jを使ってて気づいた点
• 箱庭環境なので色々と制限がある
• http://code.google.com/intl/ja/appengine/docs/java/runtim
e.html
以下、僕がハマったいくつかの点
(網羅してないので詳しくはWEBや佐藤さんの資
料などをご覧下さい。)
• ソケットつなげない
– 他のURLに飛ばせない
– URL Fetch APIというものを使えば可能らしい
GAE/Jを使ってて気づいた点
• スレッド作れない
– 他にも、待ちたいのにThread.sleepが怒られた。。
気がするが気のせいかも。。currentThreadだとい
けるのかも。少なくともObject#waitを使えば大丈
夫だった。
Object obj = new Object();
synchronized (obj) {
try {
obj.wait(3000);
} catch (InterruptedException e) {
}
}
GAE/Jを使ってて気づいた点
• エンティティにソートをかけるときに色々とあっ
た。。けど忘れました。。。
• アプリに状態を持てない
– クラスタなので(くわしくはこれを)
http://dl.google.com/io/2009/pres/W_1115_From_
Spark_Plug_To_Drive_Train_Life_of_an_App_Engi
ne_Request.pdf
GAE/Jを使ってて気づいた点
• JNDI
– javax/naming/NamingException
– このせいでSpring MVCはアノテーション周りがうまく
動かなかった。。らしい。
• システムコール使えない
• 各種サイズの制限あり
–
–
–
–
–
リクエストのサイズ
アプリのファイル数
ファイルサイズ
Datastoreのエンティティのサイズ
メール送信サイズ
などいろいろ
GAE/Jを使ってて気づいた点
• レスポンス返すまでの制限時間は30秒。
– つまり、張りっぱなしは無理
– レスポンスを返さないと
DeadlineExceededExceptionがでる
・・・・・・が、まだ最後のチャンスが一瞬(1秒以内)
残されているので、その間に直ちに返すとOK。
try {
・・・
レスポンスを返さずにだらだらする
・・・
} catch(DeadlineExceededException e) {
急いでレスポンスを返す!!!
}
GAEじゃないとダメ?
• 勿論、自分でサーバー書いてもいい
ServerSocket serverSocket = new ServerSocket(9001);
while (true) {
Socket socket = serverSocket.accept();
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
int length = in.readInt();
byte[] buffer = new byte[length];
in.read(buffer, 0, length);
Request request = Request.parseFrom(buffer);
・・・処理
Response response = Response.newBuilder().setほげほげ.build();
byte[] data = response.toByteArray();
out.writeInt(data.length);
response.writeTo(out);
in.close();
out.close();
}
GAEじゃないとダメ?
• プロセス間通信をするプロダクトを使うのも可
ex) JADEなどのマルチエージェント環境
– JADE(Java Agent Development Framework)
• LGPLのマルチエージェントのJava実装
• FIPA(http://fipa.org)で定めているマルチエージェント
の仕様を実装したもの
• Android用アドオン(JADE leap for Android)あり
PIAX for Android!?
忘れていたこと
• あ!電波は切れるんだった!!!
– ConnectivityManagerを使うといい
• ネットワークの状態が変わると、アクションに
ConnectivityManager.CONNECTIVITY_ACTIONが設定され
たインテントが飛んでくる
• このアクションに反応するBroadcastReceiverを定義する
• onReceiveでネットワークの状態変化に対応する処理を
記述
– 切れてる時は無駄に電池食わないようにお行儀よく
– 繋がったらそのことを検知してがんばる
所感
• Android
– Androidでの通信は簡単かつ楽だなあ
• シングルスレッドってところだけ気をつける
– でも電波は切れるんだったなあ
• ConnectivityManagerで
• Protocol Buffers
– JSONよりも少し面倒だなあ。
– けどメソッド名の間違いが防げる点は嬉しいかなあ。RPC部分
が付いているのが嬉しいなあ。
– Protocol BuffersのメッセージをJDOのエンティティに詰め替えず
に永続化したいなあ
アノテーションを付ければできそうだけど
• @PersistenceCapable(identityType = IdentityType.APPLICATION)
• @Persistentとか
所感
• GAE
– 色々制限あるなあ、Java版は特に厳しい?
– 特にレスポンスの待ち時間を長くしてほしいなあ
• プッシュが欲しいのです。。ないのでしょうかプッシュ。
– でも、AndroidからHTTPで動く何かを使うのならお
手軽かなあ
• ADC2にサーバーのプログラムを一緒に出しても審査
員が使い方をわからなかったら。。。という不安を防げ
る。
• インフラ落ちにくいんじゃね?