Cloud Save(Google Play App State)とは
アプリのデータをGoogleのサーバ上に保存することができます。
・保存できるデータサイズは128KB × 4スロット (最大512KB)
・データフォーマットはバイト配列
・保存できるデータサイズは128KB × 4スロット (最大512KB)
・データフォーマットはバイト配列
公式ドキュメント
Google Play Game Services — Google Developers
今回クライアント側はAndroidですが、iOS/Webアプリからも利用できるようです。
ドキュメントではGoogle Play Game Servicesの括りで紹介されていますが、ゲーム以外でも活用できるんじゃないかと思います。
Androidアプリ向けのDeveloper's Guideはこちら。
Cloud Save in Android ― Google Play Game Services — Google Developers
Cloud Saveサービス側のセットアップ
1. Google API ConsoleにアクセスしてAPI Projectを作成。
2. Servicesから[Google Play App State]を有効化。
3. API Accessから[Create an OAuth 2.0 client ID...]をクリック。
4. [Produce name]を入力して[Next]をクリック。
5. Application typeは[Installed application]を選択。
Installed application typeは[Android]を選択。
[Pacage name]/[Signing certificate fingerprint]を入力。
[Create client ID]をクリック。
そうするとClient IDがこんな感じで生成されますので、このClient IDを後述のAndroidManifest.xmlに設定します。
Androidアプリ側のセットアップ
1. Google Play servicesライブラリ(rev.7)をインストールしてEclipseにインポート
2. アプリから上記ライブラリを参照
サンプルコード
MainActivity.java
package com.example.cloudsave; import android.app.Activity; import android.content.Intent; import android.content.IntentSender; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.EditText; import android.widget.TextView; import com.google.android.gms.appstate.AppStateClient; import com.google.android.gms.appstate.OnStateLoadedListener; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks; import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener; import com.google.android.gms.common.Scopes; public class MainActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, OnStateLoadedListener, OnClickListener { private AppStateClient mAppStateClient; private boolean mConnecting; private TextView mTextView; private EditText mEditText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String[] scopes = new String[] { Scopes.APP_STATE }; mAppStateClient = new AppStateClient.Builder(this, this, this).setScopes(scopes).create(); findViewById(R.id.button_update).setOnClickListener(this); findViewById(R.id.button_load).setOnClickListener(this); mTextView = (TextView) findViewById(R.id.textview); mEditText = (EditText) findViewById(R.id.edittext); } @Override protected void onStart() { super.onStart(); if (!mConnecting) { mConnecting = true; mAppStateClient.connect(); } } @Override protected void onStop() { super.onStop(); mAppStateClient.disconnect(); } @Override public void onConnected(Bundle bundle) { mConnecting = false; mAppStateClient.loadState(this, 0); } @Override public void onDisconnected() { } @Override public void onConnectionFailed(ConnectionResult connectionResult) { if (connectionResult.hasResolution()) { try { mConnecting = true; connectionResult.startResolutionForResult(this, 0); } catch (IntentSender.SendIntentException e) { mAppStateClient.connect(); } } else { // TODO:接続失敗時の処理 } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { mAppStateClient.connect(); } } @Override public void onStateConflict(int stateKey, String resolvedVersion, byte[] localData, byte[] serverData) { mAppStateClient.resolveState(this, 0, resolvedVersion, serverData); } @Override public void onStateLoaded(int statusCode, int stateKey, byte[] data) { if (data == null) { return; } mTextView.setText("Cloud Saveサービス上のデータ:" + new String(data)); mEditText.setText(""); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button_update: String message = mEditText.getText().toString(); mAppStateClient.updateStateImmediate(this, 0, message.getBytes()); break; case R.id.button_load: mAppStateClient.loadState(this, 0); break; default: break; } } }
ざっくり解説
【データセーブ】
データのセーブはupdateStateImmediate()で行う。
更新成功するとonStateLoaded()が呼ばれる。
コンフリクト発生時はonStateConflict()が呼ばれるのでresolveState()で解決する。
【データロード】
データのロードはloadState()で行う。
読み込みが完了するとonStateLoaded()が呼ばれる。
このコードではデータのセーブにAppStateClient.updateStateImmediate()を使っていますが、AppStateClient.updateState()を使った方がバッテリー・ネットワークに優しいそうです。
【データセーブ】
データのセーブはupdateStateImmediate()で行う。
更新成功するとonStateLoaded()が呼ばれる。
コンフリクト発生時はonStateConflict()が呼ばれるのでresolveState()で解決する。
【データロード】
データのロードはloadState()で行う。
読み込みが完了するとonStateLoaded()が呼ばれる。
このコードではデータのセーブにAppStateClient.updateStateImmediate()を使っていますが、AppStateClient.updateState()を使った方がバッテリー・ネットワークに優しいそうです。
AndroidManifest.xml
※レイアウトXMLは割愛
<application> <meta-data android:name="com.google.android.gms.appstate.APP_ID" android:value="<Your Client ID>" /> </application><application>タグの中にClient IDを設定する。
※レイアウトXMLは割愛
動かしてみた
ActivityのonStart()でconnect()を読んでいるので
アプリを起動するとログインを求めるダイアログが出ます。
サンプルアプリの動きは下記の通りです。
[Update State]ボタンでEditTextに入力した内容をCloud Saveに保存
[Load State]ボタンでCloud Saveからデータを読み込みTextViewに表示
2台の端末からデータのセーブを行うケースで、ローカルの情報が古い状態でデータのセーブを行うとコンフリクトが発生します。
サンプルでは、コンフリクト発生時はサーバ側のデータを採用するようにしています。
コンフリクトの解決についての公式ドキュメントはこちらです。#まだ読んでません...
Resolving Cloud Save Conflicts | Android Developers http://developer.android.com/training/cloudsave/conflict-res.html
最後に
API Consoleを見ると、20,000,000 requests/dayとあります。
上限を超過したときってどうなるんでしょう?
ご存知の方がおられましたら教えてください。