PhoneGap(Cordova) での Android でのデバッグと StorageAPI を使ってみた件

結論

公式ドキュメントに全部載ってるし嫁
英語::Apache Cordova API Documentation
日本語::PhoneGap API Documentation
無駄にハマった感があって非常に徒労感がひどいが、まあ検証ということで勘弁して下さい >>> 各位
まああとは雑多に jQuery Moblile でいろいろ見た目をどうこうとか探してた。概要はわかったけど細かい Tips があるので知らないといろいろこれもハマる。

weinre でのデバッグ

iOS でも Android でも基本的には同じだと思う。

Cordova 2.x から node 推奨っぽくなった?

weinre 自体は Java であったらしいし今まではそれを使っていたようだけど、Cordova 2.x からは Node 推奨になったのかな?まあ Java と Node と両方パッケージがあります。
とりあえず今回は Node 環境は整えてあるのでインストールするだけ。Node のインストールは各種バイナリがあるけど自分は nvm を使っている(Node のバージョンアップが激しいことと、あんまりグローバルを汚染したくない)
Node環境を整えてCoffeeScript+lessをvim vim しちゃうメモ - AtAsAtAmAtArA
で、 weinre 自体のインストールは npm で簡単

# 場合によっては -g でグローバルにいれる
npm install weinre

あとは node_modules/weinre/weinre 以下に入るので直接そこからコマンドを叩けば実行できる。ホストやポートを変えたい場合は -h みましょう

Android で weinre をつなぐ

access origin を許可する

もし res/xml/config.xml で許可していない場合は Allow する必要がありそう。
Google Groups
ちなみに iOS の場合も origin を許可しないといけないっぽい。まだ試してはいないが、そういう情報がちらほら。

weinre デバッガを埋め込む

で、weinre 自体はデフォルトでは localhost:8080 で起動するので SafariChrome でアクセスして(Web Inspector が使える必要がある)各種情報を見ましょう。で、HTML か JS に仕込みましょう。
HTMLの場合

<script src="http://localhost:8080/target/target-script-min.js#anonymous"></script>

JS の場合

(function(e){e.setAttribute("src","http://localhost:8080/target/target-script-min.js#anonymous");document.getElementsByTagName("body")[0].appendChild(e);})(document.createElement("script"));void(0);

Android の場合は自分の IP アドレスは特別な値を持っているので、それを参照しないといけない。具体的には localhost が 10.0.2.2 になる。
参考::1台のPCでAndroidエミュレータ&サーバを開発する時に注意する事 | Happy my life
参考::AndroidのWebviewからlocalhostにアクセスする方法 - ぎじゅっやさん

<script src="http://10.0.2.2:8080/target/target-script-min.js#anonymous"></script>

weinre デバッガを埋め込んだらあとはリモートデバッグできます。*1

Cordova StorageAPI をつかってみる

今回はとりあえずストレージまわりを使いそうな予感だったのでそこを調べた。ストレージとしては主に HTML5 の localStorage をそのまま使う方法と、この StorageAPI を使う方法がある。localStorage が単なる key-value な Storage なのに対して StorageAPI をつかうと sqlite 相当のことをラップしてつかえるのかな?

結局公式ドキュメントが最強だった

自前でいちいちなんかする必要なんてなかったんや……。
参考::PhoneGap API Documentation
まあ読めばいいです。の一言で片付けるのもなんなので補足しておく。

最終的なコードは以下

まあパクりですよ

function populateDB(tx) {
    tx.executeSql('DROP TABLE IF EXISTS DEMO');
    tx.executeSql('CREATE TABLE IF NOT EXISTS DEMO (id unique, data)');
    tx.executeSql('INSERT INTO DEMO (id, data) VALUES (1, "First row")');
    tx.executeSql('INSERT INTO DEMO (id, data) VALUES (2, "Second row")');
}

function errorCB(err) {
    alert("Error processing SQL: " + err.code);
    alert("Error processing SQL: " + err.message);
}

function successCB() {
    alert("success!");
    var db = window.openDatabase("test_database", "0.1", "testDB", 1000000);
    db.transaction(queryDB, errorCB);
}

function queryDB(tx){
    tx.executeSql('select * from DEMO', [], querySuccess, errorCB);
}

function querySuccess(tx, results) {
    for (var i=0; i < results.rows.length; i++){
        alert(results.rows.item(i).id + ':' + results.rows.item(i).data)
    }
}

// Wait for Cordova to load
document.addEventListener("deviceready", onDeviceReady, false);
// Cordova is ready
function onDeviceReady() {
    var db = window.openDatabase("test_database", "0.1", "testDB", 1000000);
    db.transaction(populateDB, errorCB, successCB);

本家のサンプルや日本語のサンプルを参考にした。で、つかえるメソッドはドキュメントに準じたものが使える。大まかな流れを以下に記す

  • Cordova の deviceready イベントを Listen しておく
  • deviceready イベントをキャッチしたを発火させて、 window.openDatabase でデータベースを初期化する
    • ここの引数と内容はドキュメント読めばわかる。
  • db.transaction で 1. 発火イベント 2. エラーコールバック関数 3. 成功コールバック関数 を呼ぶ
    • ドキュメント読めばわかるが、エラーコールバック関数では err オブジェクトなりをとって code, message プロパティを呼んでやることができる
    • ドキュメント読めばわかるが、成功コールバック関数で発火イベントが成功したときのコールバック関数を呼ぶことができる。INSERT 相当のことなら「よくできました!」程度でいいかもしれないが、SELECT でとってくるときはトランザクションで管理して成功した内容を引き継いで新たにイベントを発火させることになる
  • 発火イベントで仮引数 tx をとることができる。ここで使用できるのは executeSql という生 SQL を文字列で呼んでやることになる
    • なにかもうちょっと ORM ではないがラップできるものがないか探したけど見当たらなかった。もしかしたらライブラリにあるのかもしれないのだけど。
  • SELECT した結果をとりたい場合はまた db.transaction をつかって連鎖させる必要がある
  • ここでも tx という仮引数をとって生の SQL を発行する。 [] ってのはよくわからない。で、発火イベントとエラーコールバック関数を引数としてとってやる
  • で、発火イベントで tx, results という仮引数をとってやって results で中身をとることができる。詳しくはドキュメント嫁
    • results に各種結果がはいってる。
    • results.rows に取得したデータ要素がはいってる
    • 具体的には results.rows.length で要素数、 results.rows.item(i).id で id, results.rows.item(i).data でデータがとれる。

まあ

「ドキュメント嫁」を冗長に解説するとこんな感じになります。逆に言うと公式ドキュメントが丁寧なのでそれさえちゃんと読んでればほかの API をたたく場合でもそんなにハマらなくてもすみそうだな、という温度感を得ることができたのは収穫かもしれない。

*1:さらっと書いているけど、ここらへんでけっこうハマっていた……