このブログを作成するシステムはgdataライブラリを利用して、いくつかの手製スクリプトで構成されています。
Subversionで履歴を管理していたのですが、実際にはほとんど使えないデータだったので履歴を切り離してGitに移行してしまいました。 まぁcommitしてきたログがあるので移行しても良かったのですが、気分的にはすっきりしていい感じです。
さて、新しい仕事を始めてからは.Net FrameworkやらAndroidやらiPhoneなんかのプログラミングを始めていますが、今回はJSONICというJavaでJSONデータを扱うライブラリについてです。
AndroidでのJSONデータの扱い
さて、Androidではorg.jsonパッケージがついてきて最低限のJSONデータを取り扱う環境があります。
送信用にJavaのデータ構造をJSONにする際には、特に問題なく扱えると思います。 反対に外部から受け取ったデータを扱う際にはローレベル過ぎて扱いが少しだけ面倒です。
今回はorg.jsonパッケージの上にライブラリを構築するのではなく、JSONICというJava言語用のJSONライブラリを利用する事にしました。
作成した成果物はjsonic4androidとしてGithubで公開しています。
JSONICを扱うメリット
基本的には無理に使う必要はありません。
自作の郵便番号検索システムから受け取ったJSONデータをJavaBeansとして扱うために使用しています。
この部分をorg.jsonパッケージを利用して取り出そうとして、面倒だったのは次のような点です。
- UTF-8にエンコードするため、BufferedReaderを使い全データを変換する必要があった
- 配列の中に各データのレコードを持っているため、要素名を指定してアクセスする処理が冗長で、JavaBeansにマッピングする必要があった
- org.jsonパッケージを利用する自作変換クラスが安定して動作しなかった (これは自分の技量の問題です)
JSONのデータ構造は単純ですが項目数が多いため、各要素にアクセスするコードは繰り返しが多く、JavaBeansに変換してから扱うのがベストに思えたのですが、その変換コードを書くのがさらに面倒だったというのが実情です。
エントリ数が10以下で配列の中にさらにJSONデータ構造を持つような場合でなければ、org.jsonパッケージをそのまま使うのがお勧めです。
Android端末向けのプログラミングではライブラリを利用する事も作業量を減らす上では大切ですが、アプリ全体のパッケージサイズの圧縮が必要だったり、インタフェースを使わないなどのいくつかのプログラミング上のテクニックが存在します。
そういった事との兼ね合いで常にベストな選択をしてください。
パフォーマンス
jsonicでBufferedInputStreamからJavaBeansオブジェクト(JPostalBeanインスタンス)を生成した場合と、自前でInputStreamを開いてUTF-8に変換した文字列からJavaBeansオブジェクトを返すメソッドを作成して変換時間を計測して比較してみました。
is = urlconn.getInputStream();
bis = new BufferedInputStream(is);
long beginTime = new Date().getTime();
ret = JSON.decode(bis, JPostalBean.class);
long endTime = new Date().getTime();
is = urlconn.getInputStream();
br = new BufferedReader(new java.io.InputStreamReader(is,"UTF-8"));
StringBuilder reqText = new StringBuilder();
...
long beginTime = new Date().getTime();
ret = parseJsonStream(reqText.toString());
long endTime = new Date().getTime();
合計で170件のデータを含むデータを処理しましたが、jsonicを利用した場合は約10%ほど高速でした。 自前ライブラリの場合には、BufferedReaderを使いUTF-8文字列を取得するところは省いていますが、それを含めると2倍近く処理速度の差が確認できました。
jsonicを使ったから特に処理が遅くなるという事ではなさそうなので、全体のバランスの中で選択すれば良いのかなと思います。
パッケージサイズの増加量
郵便番号検索アプリで作成したAPKファイルで比較するために、jsonicに依存するコードをコメントアウトしてjsonic4androidライブラリプロジェクトへのリンクを削除したAPKファイルを作成してみました。
$ ls -l JPostalSearch.*.apk -rw-r--r-- 1 yasu yasu 469461 2012-04-01 10:38 /home/yasu/JPostalSearch.16e.without_jsonic.apk
jsonic4androidを含む、通常のAPKファイルは次のようになります。
$ ls -l JPostalSearch.*.apk -rw-r--r-- 1 yasu yasu 499148 2012-04-01 10:45 /home/yasu/JPostalSearch.16e.apk
およそ30KB程度の増加量になり、配布について大きな障害ではなくなります。
jsonic4androidが対応するSDK(API)バージョン
作成しているアプリはAndroid 1.6 から(minSdkVersion="4")対応するようにエミュレータで動作を確認しています。
実機で確認しているのはAndroid 2.3.3 (SdkVersion="8")、及び、Android 3.2 (SdkVersion="13") です。
変更内容は次のセクションを参照してください。
jsonic4android - Eclipseプロジェクト形式での配布
今回扱ったJSONICのバージョンは1.2.10です。当初は1.2.9でしたがバージョンアップしたため、Githubのjsonic4androidも追随しました。
Githubに登録してあるのはEclipseに組み込む事を前提としたインポート可能なパッケージ形式です。
GithubのプロジェクトページのWikiにアクセスすると、画像で使い方が並んでいます。 gitでcloneしてから「Existing Projects into Workspace」を選ぶだけで、Eclipseの中で利用する事ができます。
jsonic4androidとして変更点
Eclipseパッケージとして組み込めるような形式になっている他の変更は次のような点です。
SDKのバージョンでいうと4、いわゆるAndroid 1.6以降で扱えるように、主にString::isEmptyメソッドを使わないように修正しています。
jsonicとproguardとの関係
リリースする際には署名をしますが、ここで通常はProguardを使う事になります。
開発環境ではうまく動くのに、署名したAPKファイルを配布すると問題になる場合がある原因の一つにはProguardがあります。
Proguardでの課題はテンプレートプログラミングを行なうためのGenericsとの相性です。
Proguardはパッケージ名やメソッドなどを変更してしまいますが、Java Reflection APIを扱うJSONICからはメソッドの名称が変更されてしまうと変数にアクセスする事ができなくなってしまいます。
鉤括弧でクラスを指定するGenericsの形式を使う場合には、明示的にproguard.cfgでそのオブジェクトを対象外にする必要があります。
今回はJPostalBeanクラスをjsonicのdecodeメソッドで処理しているため、proguard.cfgに次のような記述を追加しました。
-keep class net.yadiary.android.jpostal.beans.JPostalBean {
<init>(...);
*;
}
さいごに
JSONはクライアントとサーバーが1対1に対応するような軽量Webサービスを利用する場合の選択肢としては最有力だと思います。
SOAPでなければいけない場合は、ステートフルなセッションを行ない、メッセージボディを途中で分割して別々のサーバーに送信するSOPA対応Proxyを使用しなければいけない、限られた環境に限定されるでしょう。
Androidのようなスマートフォンで外部とのメッセージをやりとりする場合には自然に使う事になるJSONですが、アクセスするためにJSONICのようなデコーダとJavaBeansのデータ構造を利用しないと、JSONのデータ構造が変化する度にコードを追加、修正する手間が発生する事になります。
選択肢は沢山あるので、うまくライブラリを利用して見通しの良いコードを書くようにしたいものです。
0 件のコメント:
コメントを投稿