Edited at

[Parse.com]オフラインでもクラウドにデータを保存できる!データ操作系メソッド | Android

More than 5 years have passed since last update.


追記(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
}
}
});

というわけで、やりたいこと別のデータ操作系メソッドまとめでした。