追記(2014/05/01)
2014/05/01、ParseのAndroid SDKでLocal Datastoreという機構がリリースされました。
Parse Blog: Take Your App Offline with Parse Local Datastore
ローカルに保存することを pin
、ローカルから削除することを unPin
と呼ぶようですね。
新しいSDKでは、saveEventually
を実行すると、クラウドに保存されるまでは自動的に pin
され、ParseQuery#fromPin
でそれを取得できるようです。
いままでは、saveEventually
が完了するまでの間にそのデータを使いたいときは、自分で用意したローカルのsqliteなどに保存しておかなければならなかったのですが、これで不要になりますね。
この新機能自体は以下のエントリに特に影響はありません。
それにしてもParseの進化のスピードが早過ぎる....(;´∀`)
環境等(2014/04/24時点)
Parse Android SDK 1.4.3 Download
[おまけ]
Nifry Mobile Backend(NCMB)は、この投稿の「Parse」を「NCMB」に置き換えればそのまま動きます。
Parseのデータ操作系メソッド
Parseは流行りのBaasの中でもダントツの勢いのサービスで、目玉機能のひとつがデータのクラウド保存です。
データをクラウドに簡単に保存できれば、ユーザ間のデータ連携などが容易になります。夢が広がりんぐ。
という訳で、データの保存・更新・取得・削除についてまとめます。ParseのSDKはスマホやタブレットがオフラインの場合でも動作するようある程度考慮されていますので、「電波が不安定だといちいち固まるアプリは作りたくない!」という人も安心ですよ。
予備知識:ParseObject
ParseではデータのかたまりをParseObject
クラスのオブジェクトとして表します。
RDBのレコードを表すDataBeanをイメージすればよいでしょう。(ParseはスキーマレスなKey-Valueデータストアなのでちょっと違いはありますが)
このParseObject
クラス自体が保存・更新・削除といったメソッドを備えています。
ParseObject gameScore = new ParseObject("GameScore");
gameScore.put("score", 1337);
gameScore.put("playerName", "Sean Plott");
gameScore.saveInBackground();
上のソースコードでは、new ParseObject()
で新たなParseObject
を作成し、ParseObject#put
メソッドでデータをセット、ParseObject#saveInBackground
メソッドでParseのクラウドに保存しています。
保存(saveXX系)
まずはデータの保存から。
Parseでは登録と更新は(API上)特に区別されておらず、いずれもsaveと表現されています。キー(Object ID)が一致するデータが既に存在すれば更新、なければ登録されます。
とりあえず簡単に保存したいとき:ParseObject#saveInBackground
ParseObject gameScore = new ParseObject("GameScore");
gameScore.put("score", 1337);
gameScore.put("playerName", "Sean Plott");
gameScore.saveInBackground();
saveInBackground
メソッドは名前の通り、バックグラウンドで保存を行います。
この例では"GameScore"クラス(クラスとはRDBでいうテーブル)のオブジェクトを作成し、バックグラウンドスレッドで保存を実行しています。
parse.comのデータブラウザ(https://www.parse.com/apps/アプリ名/collections) でGameScoreを選ぶとデータが保存されているはずです。
AndroidはUIスレッドでのネットワーク通信が禁止されていますが、AsyncTaskLoader等を使わず簡単にバックグラウンド処理が実装できます。
超便利。
保存するときに、RDBのように事前にスキーマ(テーブル定義)を決めておく必要はありません。 ParseObject#put
で指定したKeyで自動的にカラムが作られます。
アプリの機能追加などで、簡単にカラムを増やせますね!!!
(とは言え通常は同じクラスのデータは同じKeyを持っている(べき)でしょう。レコードごとにカラムが違うとかなり面倒です)
このメソッドは「保存してねー」というリクエストを投げて終わりなので、保存に失敗しても分かりません。困るときは次のメソッドを使います。
保存後になにかしたい、保存が成功したか知りたいとき:ParseObject#saveInBackground(Callback)
ParseObject gameScore = new ParseObject("GameScore");
gameScore.put("score", 1337);
gameScore.put("playerName", "Sean Plott");
gameScore.saveInBackground(new SaveCallback() {
public void done(ParseException e) {
// ここは保存完了後に呼び出される。
if (e == null) {
System.out.println("保存成功!");
// ここに次の処理を書く。画面の更新や、次のデータの保存など。
} else {
System.out.println("保存失敗(´・ω・`)");
Log.e(TAG, null, e);
}
}
});
保存後に次の処理を実行したい場合には、savaInBackground
メソッドにSaveCallback
クラスのインスタンスを渡し、 SaveCallback#done
メソッドに処理を実装します。
保存処理の実行完了後に、doneメソッドが呼ばれます。引数の ParseException
がnullの場合は保存成功、nullでなければ失敗です。端末がオフラインの場合などにExceptionが発生します。
オフラインでも保存したいとき:ParseObject#saveEventually
gameScore.saveEventually();
saveInBackgroundとの違いは、こちらはオフラインでもExceltionが発生しない点です。オフラインの場合は、次にオンラインになった時に保存処理が走ります。オフラインのままアプリが終了したら、次回起動時にリトライします。
saveEventuallyが呼ばれた順番も保証されるため、保存する順番に意味があるときや、「とにかく最終値が保存されていてほしい。(最終値さえ保存されていればよい。)」という時に使えます。
まさにEventually。
気のせいかもしれませんが、オンラインの場合でもInBackgroundよりEventuallyの方が保存されるまでに時間が掛かる気がします…(たぶん実際そう。知ってる人おしえて)。
なので私はオンラインならInBackground、オフラインならEventuallyというように場合分けしています。
オフラインでもいつか保存できればいいけど、保存完了したのは知りたいとき:ParseObject#saveEventually(Callback)
gameScore.saveEventually(new SaveCallback() {
public void done(ParseException e) {
// ここは保存完了後に呼び出される。
if (e == null) {
System.out.println("保存成功!");
// ここに次の処理を書く。画面の更新や、次のデータの保存など。
} else {
System.out.println("保存失敗(´・ω・`)");
Log.e(TAG, null, e);
}
}
});
スレッドをブロックしたいとき(同期処理):ParseObject#save
gameScore.save();
InBackground, Eventuallyはバックグラウンドスレッドで非同期に実行されますが、同期処理を行いたいときは ParseObject#save
を使用します。
上の方でも書いたとおり、UIスレッドではこのメソッドも使えないため、別スレッドで実行します。保存処理が完了するまでそのスレッドはブロックされます。
削除(deleteXX系)、取得(getXX系, findXX系, fetchXX系)
どれもsaveXX系と考え方は同じです。
ただし、getXX, findXXはParseObjectでなく ParseQuery
クラスのメソッドです。
ParseQuery<ParseObject> query = ParseQuery.getQuery("GameScore");
query.getInBackground("xWMyZ4YEGZ", new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e) {
if (e == null) {
// object will be your game score
} else {
// something went wrong
}
}
});
というわけで、やりたいこと別のデータ操作系メソッドまとめでした。