Android AccountManager+SyncAdapter
以前Android AccountManagerで使えるアカウントを開発する方法を書きましたが同期的な機能のSyncAdapterをすっぽかしてたのでやってみた
その前に現状だとSyncAdapterを使える仕様じゃないので、そこら辺をまず修正しなければならない。SyncAdapterを使う為にはContent Providerが必要な模様、なのでアクティビティ等のアプリに関わる部分(AccountAuthenticator以外)からサーバーに通信してデータを取る部分を全てContent Provider経由で取る。でバックグラウンドで同期が行われる際に、サーバー側から取得したデータをSQLiteデータベースに保管しておいて、それをContent Providerでやり取りをする
という感じな仕様に変えないといけない。んまぁそこら辺はちゃちゃっと修正して、SyncAdapterの本体辺りな所だけ書く
SyncAdapterを使う手順
- AndroidManifest.xmlにandroid.content.SyncAdapterを持つ
を定義。さらにSyncAdapterから使うContent Providerの定義( )も必要 - SyncAdapterなXMLリソース定義を作成
- SyncAdapterなandroid.app.Serviceを実装
- SyncAdapterなandroid.content.AbstractThreadedSyncAdapterを実装
以上。これだけの追加が必要。でアプリ側の処理フロー的な流れ的には
- SyncAdapterによるサーバーとのデータの同期を行う。その際そこからContent Providerを通じてSQLiteにデータを保管
- Activity等からデータを利用するにはサーバーに通信せずにContent Providerを仲介する。でその際に必要になるのがCursorの扱いだが、そこはCursorLoader等を用いて利用する
っていう感じ。これは上でも書いてるので
又、SyncAdapterな機能を使う際には
- android.permission.READ_SYNC_STATS
- android.permission.READ_SYNC_SETTINGS
- android.permission.WRITE_SYNC_SETTINGS
っていう権限が必要になる模様
AndroidManifest.xmlを修正する
SyncAdapterな所を追加する。上記でも書いてるようにパーミッションな所はすっ飛ばす
<service android:name=".SampleSyncService" android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync" />
</service>
<provider
android:name=".SampleContentProvider"
android:authorities="@string/provider_name"
android:label="@string/provider_name"
android:exported="false"
android:permission="sample.provider.permission" />
res/xml/sync.xmlを作成
<?xml version="1.0" ?>
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="@string/account_type"
android:contentAuthority="@string/provider_name" />
http://developer.android.com/reference/android/content/AbstractThreadedSyncAdapter.html に書いてるのでそれ参考に
SampleSyncService.java
package kinjouj.sample.authadapter;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class SampleSyncService extends Service {
private static final String TAG = SampleSyncService.class.getName();
private SampleSyncAdapter mSyncAdadter;
@Override
public void onCreate() {
super.onCreate();
mSyncAdadter = new SampleSyncAdapter(getApplicationContext(), true);
}
@Override
public IBinder onBind(Intent intent) {
Log.v(TAG, "onBind");
return mSyncAdadter.getSyncAdapterBinder();
}
}
AbstractThreadedSyncAdapter#getSyncAdapterBinderを返すだけ
SampleSyncAdapter.java
package kinjouj.sample.authadapter;
import java.io.IOException;
import java.util.Date;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;
import android.util.Log;
public class SampleSyncAdapter extends AbstractThreadedSyncAdapter {
private static final String TAG = SampleSyncAdapter.class.getName();
private AccountManager mAccountManager;
public SampleSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
mAccountManager = AccountManager.get(context);
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult result) {
Log.v(TAG, "onPerformSync");
Log.v("sync", "sync: " + new Date());
String authToken = getAuthToken(account);
// ここらへんでサーバーからのデータを取得してContent Provider介してSQLiteに対する保管を行う
}
public String getAuthToken(Account account) {
String authTokenType = getContext().getString(R.string.account_type);
String authToken = null;
try {
authToken = mAccountManager.blockingGetAuthToken(account, authTokenType, true);
} catch (OperationCanceledException e) {
e.printStackTrace();
} catch (AuthenticatorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return authToken;
}
}
で大量のデータを登録するような場合だと、ContentProvider.applyBatch辺りを使えば良いんじゃないかなーっと
以上。アプリ入れてアカウント作ると
というように同期なチェックが出来るようになる。チェック入れると30秒毎にonPerformSyncが実行されてデータの同期が行われる。でこの間隔を設定にはどうしたらいいのかっていう所はまだ判明してない
んまぁAndroid公式のサンプルにあるSampleSyncAdapterのソース読んだ方が早いかも