Android SyncAdapter Automatically Initialize Syncing

Dandre Allison picture Dandre Allison · Jun 18, 2012 · Viewed 14.1k times · Source

I have a SyncAdapter for my app, and an AccountManager to add my apps accounts to the Android Account Manager. My code for when I add an account to the Account Manager looks like:

Bundle data = new Bundle(5);
data.putString(_PEOPLE_ID, people_id);
data.putString(_FIRST_NAME, first_name);
data.putString(_LAST_NAME, last_name);
data.putString(_PLAN, plan);
data.putString(_BIRTHDAY, birthday);
Account account = new Account(username, _ACCOUNT_TYPE);
try {
    boolean created;
    created = _account_manager.addAccountExplicitly(account,
                                   _cryptography.encrypt(_SEED, password), data);
    response.accountCreated(created);
    _account_manager.setAuthToken(account, _TOKEN_TYPE, session_token);
    _model.updateActiveAccount(people_id, username, password);
    SharedPreferences.Editor settings = _settings.edit();
    settings.putString(_ACCOUNT_TYPE, account.name);
    settings.putString(_TOKEN_TYPE, session_token);
    settings.commit();
    // Tells the content provider that it can sync this account
    ContentResolver.setIsSyncable(account, AUTHORITY, 1);
    final Bundle extras = new Bundle(1);
    extras.putBoolean(SYNC_EXTRAS_INITIALIZE, true);
    ContentResolver.addPeriodicSync(account, AUTHORITY, extras, 900);
} catch (Exception e) {
    Ln.e(e.getCause());
}

I can add the account to the Account Manager successfully through Settings, but I have to manually enable syncing for the account in Settings also, even though background data and sync automatically settings are enabled on the emulator. If I manually enable syncing, then the sync is performed fine, it just isn't started by default.

Answer

Dandre Allison picture Dandre Allison · Jun 19, 2012
ContentResolver.setIsSyncable(account, AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

As Blehi said will initiate the automatic syncing of the given account, given the global Settings, "Background data" and "Auto-sync" are enabled.

To prevent the back-to-back syncing (from jcwenger) make sure that if any method in the SyncAdapter.onPerformSync(...) calls ContentResolver.notifyChange(...) it uses ContentResolver.notifyChange(uri, observer, false) to flag the notify not to trigger a sync call (the third parameter is syncToNetwork).

If you're using the ContentProvider to perform the inserts/deletes/updates for you SyncAdapter it makes sense to call ContentResolver.notifyChange(...) so that while the app is visible the user can receive updates from the SyncAdapter, which means you will have the ContentProvider making ContentResolver.notifyChange(...) calls. To get this set up to work, I've added (following the dev guide) CALLER_IS_SYNC_ADAPTER query parameter to every URI that is used for the SyncAdapter. Add this method to the ContentProvider to test the incoming URIs

/**
 * Determines if the given URI specifies that the request is coming from the sync adapter.
 * @param uri the given URI
 * @return true if the uri specifies that the request is coming from the sync adapter
 */
private boolean callerIsSyncAdapter(Uri uri) {
    final String is_sync_adapter = uri.getQueryParameter(CALLER_IS_SYNC_ADAPTER);
    return is_sync_adapter != null && !is_sync_adapter.equals("0");
}  

then you can do

getContext().getContentResolver().notifyChange(uri, observer, !callerIsSyncAdapter(uri));

whenever you need send a change notification.

And if you want to schedule the sync to be executed periodically at a frequency (polling the server) add this with the ContentResolver.setSyncAutomatically(...) call.

ContentResolver.addPeriodicSync(account, AUTHORITY, new Bundle(), frequency_in_seconds)