概要
Android には SyncAdapter というデータの同期のためのフレームワークがあります。このフレームワークにのっかることで、通信環境やサーバの状態に応じた適切なデータ同期タイミングの調整、リトライのバックオフをシステムがやってくれます。そのためアプリ側の実装では、どのデータをどのように同期するかというロジックに集中することができます。
必要なものとしてはAccount
とContentProvider
、Service
そしてAbstractThreadedSyncAdapter
の4つです。ContentProvider
は空実装でも問題ありません。またAccount
もダミーのもので構いません。いちばん大事なのはAbstractThreadedSyncAdapter
を継承して作る「どのデータをどのように同期するか」という部分です。
onPerformSync
メソッドのSyncResult
Android システムは、いい感じのタイミングを見計らって、有効なAbstractThreadedSyncAdapter
のonPerformSync
メソッドをバックグラウンドスレッドから呼び出してくれます。ここで同期したいデータを取り出し、適切な手順でサーバ(クラウド)とデータを同期します。
何もなければメソッドは正常に戻り、アプリがなすべき同期のフローは終わりです。ただし、通信の状況やサーバの状況によっては、うまくデータが同期できないことも考えられます。そのようなときのために、onPerformSync
メソッドにはSyncResult
というオブジェクトが渡されます。
Android Developers のリファレンスによると
This class is used to communicate the results of a sync operation to the SyncManager. Based on the values here the SyncManager will determine the disposition of the sync and whether or not a new sync operation needs to be scheduled in the future.
とあり、Android システムがデータの同期のスケジューリングについてSyncResult
の内容を見て判断してくれることが分かります。
SyncResult.stats
SyncResult
のリファレンスには各々のフィールドについて、システムが無視するものと使うもの、参考程度にするものとがあると書いてあります。
このうち、システムが同期のスケジューリングのときに使う大事な情報をもつものはstats
フィールドです。このフィールドはSyncStats
という型で、何件のデータを同期したか、何件のデータが挿入・上書き・削除・されたか、また何件のIOException/ParseException/AuthExceptionがあったかを記録します。
名前からもわかるとおり、何かしらの理由で同期に失敗した場合には、IOException/ParseException/AuthExceptionの件数をインクリメントすることで、システムにスケジューリングを促すことになります。
ソフトエラーとハードエラー
同期の失敗理由にはいくつかの種類があるはずです。通信が貧弱でうまくデータがやりとりできなかった場合、サーバが障害等でレスポンスできなかった場合、あるいは、アプリ側の不良が理由で同期に失敗することもありえます。
通信環境による失敗はソフトエラー
としてシステムが記録し、近い未来に同期を再実行するようスケジューリングします。これが何度も続いた場合にはエクスポネンシャルバックオフと呼ばれる方法で、スケジューリングの感覚が徐々に長くなっていきます。
一方で、サーバの障害やアプリの不良、または不正なデータはすぐには改善する見込みが無いためハードエラー
としてシステムが記録します。このハードエラーが何度も続くと、システムは同期のスケジューリングを諦めます。
SyncResult
をちゃんと記録しないとどうなるか
SyncResult
に対して同期のフローで何が起きたかを記録しない場合、システムは同期が正常に終了したものとみなします。onPerformSync
メソッドにthrows
シグネチャはないので、自分で適切に例外をハンドリングしSyncResult
にエラーを記録する必要があります。仮になにもしないと、たとえ何らかの失敗があってもシステムはスケジューリングをし続ける事になります。
君がッ 泣いても 殴るのをやめないッ!
記録のとりかたについては、GoogleSamples にちょうどよいプロジェクトが公開されています。
余談:ContentProviderClient
は何だ
onPerformSync
メソッドにはSyncResult
以外にもContentProviderClient
も渡されます。
これはContentResolver
のようにContentProvider
にクエリを投げたりデータを挿入したりするために使います。これらの違いなどについては、このブログ記事に詳しく解説されていますが、AbstractThreadedSyncAdapter
においてはContentProviderClient#release()
はonPerformSync
から戻ってきたときに呼び出してくれるようです。