SyncAdapter
。SyncAdapter
ContentProvider
Config.java
MANIFEST_URL
RemoteConferenceDataFetcher
fetchConfenceDataIfNewer
public class RemoteConferenceDataFetcher { // (...) public String[] fetchConferenceDataIfNewer(String refTimestamp) throws IOException { BasicHttpClient httpClient = new BasicHttpClient(); httpClient.setRequestLogger(mQuietLogger); // (...) // データが refTimestamp より新しいときのみダウンロード if (!TextUtils.isEmpty(refTimestamp) && TimeUtils .isValidFormatForIfModifiedSinceHeader(refTimestamp)) { httpClient.addHeader("If-Modified-Since", refTimestamp); } } HttpResponse response = httpClient.get(mManifestUrl, null); int status = response.getStatus(); if (status == HttpURLConnection.HTTP_OK) { // データに変更があったので処理する } else if (status == HttpURLConnection.HTTP_NOT_MODIFIED) { // サーバーのデータに変更はないので何もしない return null; } else { // (エラー処理) } } // (...) }
If-Modified-Since
HTTP_OK
HTTP_NOT_MODIFIED
{ "format": "iosched-json-v1", "data_files": [ "past_io_videolibrary_v5.json", "experts_v11.json", "hashtags_v8.json", "blocks_v10.json", "map_v11.json", "keynote_v10.json", "partners_v2.json", "session_data_v2.681.json" ] }
processManifest
public class Speaker { public String id; public String publicPlusId; public String bio; public String name; public String company; public String plusoneUrl; public String thumbnailUrl; public String getImportHashcode() { StringBuilder sb = new StringBuilder(); sb.append("id").append(id == null ? "" : id) .append("publicPlusId") .append(publicPlusId == null ? "" : publicPlusId) .append("bio") .append(bio == null ? "" : bio) .append("name") .append(name == null ? "" : name) .append("company") .append(company== null ? "" : company) .append("plusoneUrl") .append(plusoneUrl == null ? "" : plusoneUrl) .append("thumbnailUrl") .append(thumbnailUrl == null ? "" : thumbnailUrl); String result = sb.toString(); return String.format(Locale.US, "%08x%08x", result.hashCode(), result.length()); } }
ContentProviderOperation
SpeakersHandler
makeContentProviderOperations
public class SpeakersHandler extends JSONHandler { private HashMap mSpeakers = new HashMap(); // (...) @Override public void makeContentProviderOperations(ArrayList list) { // (...) int updatedSpeakers = 0; for (Speaker speaker : mSpeakers.values()) { String hashCode = speaker.getImportHashcode(); speakersToKeep.add(speaker.id); if (!isIncrementalUpdate || !speakerHashcodes.containsKey(speaker.id) || !speakerHashcodes.get(speaker.id).equals(hashCode)) { // スピーカーが更新されているので、ContentProviderOperation を発行する ++updatedSpeakers; boolean isNew = !isIncrementalUpdate || !speakerHashcodes.containsKey(speaker.id); buildSpeaker(isNew, speaker, list); } } // 古いスピーカーを削除 int deletedSpeakers = 0; if (isIncrementalUpdate) { for (String speakerId : speakerHashcodes.keySet()) { if (!speakersToKeep.contains(speakerId)) { buildDeleteOperation(speakerId, list); ++deletedSpeakers; } } } }
buildSpeaker()
buildDeleteOperation()
。ContentProvider
、ContentProviderOperation
try
catch
SyncHelper
performSync()
// リモート同期を構成するタスク。 // 1 つづつ実行される(どれかが失敗しても他に影響を与えない)。 final int OP_REMOTE_SYNC = 0; final int OP_USER_SCHEDULE_SYNC = 1; final int OP_USER_FEEDBACK_SYNC = 2; int[] opsToPerform = userDataOnly ? new int[] { OP_USER_SCHEDULE_SYNC } : new int[] { OP_REMOTE_SYNC, OP_USER_SCHEDULE_SYNC, OP_USER_FEEDBACK_SYNC}; for (int op : opsToPerform) { try { switch (op) { case OP_REMOTE_SYNC: dataChanged |= doRemoteSync(); break; case OP_USER_SCHEDULE_SYNC: dataChanged |= doUserScheduleSync(account.name); break; case OP_USER_FEEDBACK_SYNC: doUserFeedbackSync(); break; } } catch (AuthException ex) { // (... 認証エラーの後始末 ...) } catch (Throwable throwable) { // (... その他のエラーの後始末 ...) // 例外の発生をシステムに知らせる if (syncResult != null && syncResult.stats != null) { ++syncResult.stats.numIoExceptions; } } }
syncResult.stats.numIoExceptions
GCMIntentService
public class GCMIntentService extends GCMBaseIntentService { private static final String TAG = makeLogTag("GCM"); private static final Map MESSAGE_RECEIVERS; static { // 既知のメッセージと GCM メッセージの受信部 Map receivers = new HashMap(); receivers.put("test", new TestCommand()); receivers.put("announcement", new AnnouncementCommand()); receivers.put("sync_schedule", new SyncCommand()); receivers.put("sync_user", new SyncUserCommand()); receivers.put("notification", new NotificationCommand()); MESSAGE_RECEIVERS = Collections.unmodifiableMap(receivers); } // (...) @Override protected void onMessage(Context context, Intent intent) { String action = intent.getStringExtra("action"); String extraData = intent.getStringExtra("extraData"); LOGD(TAG, "Got GCM message, action=" + action + ", extraData=" + extraData); if (action == null) { LOGE(TAG, "Message received without command action"); return; } action = action.toLowerCase(); GCMCommand command = MESSAGE_RECEIVERS.get(action); if (command == null) { LOGE(TAG, "Unknown command received: " + action); } else { command.execute(this, action, extraData); } } // (...) }
onMessage()
action
sync_schedule
SyncCommand
jitter
doUserScheduleSync()