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

## 追記（2014/05/01）
2014/05/01、ParseのAndroid SDKでLocal Datastoreという機構がリリースされました。
[Parse Blog: Take Your App Offline with Parse Local Datastore](http://blog.parse.com/2014/04/30/take-your-app-offline-with-parse-local-datastore/?mkt_tok=3RkMMJWWfF9wsRonuazAZKXonjHpfsX76e0rUKS3lMI%2F0ER3fOvrPUfGjI4ATsRkI%2BSLDwEYGJlv6SgFTbHGMblmy7gNUxU%3D)

ローカルに保存することを `pin`、ローカルから削除することを `unPin` と呼ぶようですね。
新しいSDKでは、`saveEventually`を実行すると、クラウドに保存されるまでは自動的に `pin` され、`ParseQuery#fromPin` でそれを取得できるようです。

いままでは、`saveEventually` が完了するまでの間にそのデータを使いたいときは、自分で用意したローカルのsqliteなどに保存しておかなければならなかったのですが、これで不要になりますね。

この新機能自体は以下のエントリに特に影響はありません。

それにしてもParseの進化のスピードが早過ぎる....(；´∀｀)

# 環境等（2014/04/24時点）
Parse Android SDK 1.4.3 [Download](https://parse.com/docs/downloads)
[おまけ]
Nifry Mobile Backend(NCMB)は、この投稿の「Parse」を「NCMB」に置き換えればそのまま動きます。

# Parseのデータ操作系メソッド

[Parse](http://parse.com)は流行りのBaasの中でもダントツの勢いのサービスで、目玉機能のひとつがデータのクラウド保存です。

データをクラウドに簡単に保存できれば、ユーザ間のデータ連携などが容易になります。夢が広がりんぐ。

という訳で、データの保存・更新・取得・削除についてまとめます。ParseのSDKはスマホやタブレットがオフラインの場合でも動作するようある程度考慮されていますので、「電波が不安定だといちいち固まるアプリは作りたくない！」という人も安心ですよ。

## 予備知識：ParseObject

Parseではデータのかたまりを`ParseObject`クラスのオブジェクトとして表します。
RDBのレコードを表すDataBeanをイメージすればよいでしょう。（ParseはスキーマレスなKey-Valueデータストアなのでちょっと違いはありますが）

この`ParseObject`クラス自体が保存・更新・削除といったメソッドを備えています。

```java
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

```java
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)

```java
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

```java
gameScore.saveEventually();

```
saveInBackgroundとの違いは、こちらはオフラインでもExceltionが発生しない点です。オフラインの場合は、次にオンラインになった時に保存処理が走ります。オフラインのままアプリが終了したら、次回起動時にリトライします。

saveEventuallyが呼ばれた順番も保証されるため、保存する順番に意味があるときや、「とにかく最終値が保存されていてほしい。（最終値さえ保存されていればよい。）」という時に使えます。
まさにEventually。

気のせいかもしれませんが、オンラインの場合でもInBackgroundよりEventuallyの方が保存されるまでに時間が掛かる気がします…（たぶん実際そう。知ってる人おしえて）。
なので私はオンラインならInBackground、オフラインならEventuallyというように場合分けしています。


### オフラインでもいつか保存できればいいけど、保存完了したのは知りたいとき：ParseObject#saveEventually(Callback)

```java
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

```java
gameScore.save();
```

InBackground, Eventuallyはバックグラウンドスレッドで非同期に実行されますが、同期処理を行いたいときは `ParseObject#save` を使用します。

上の方でも書いたとおり、UIスレッドではこのメソッドも使えないため、別スレッドで実行します。保存処理が完了するまでそのスレッドはブロックされます。



## 削除（deleteXX系）、取得（getXX系, findXX系, fetchXX系）

どれもsaveXX系と考え方は同じです。
ただし、getXX, findXXはParseObjectでなく `ParseQuery` クラスのメソッドです。

```java
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
    }
  }
});
```

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