Android SyncAdapter Automatically Initialize Syncing

14,142

Solution 1

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)

Solution 2

Just to emphasise, it seems that addPeriodicSync() needs setSyncAutomatically(), even though the documentation says that setSyncAutomatically() is only for detecting network tickles.

Note that the period will be corrected to >60s if it's less than a minute.

Solution 3

You have to set the account to be syncable by default:

ContentResolver.setIsSyncable(account, AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

I use the above 2 lines and it is working properly.

Solution 4

Initially create an account and check the sync support for the same using AccountManager. If the account supports sync then call setIsSyncable() and setSyncAutomatically().

AccountManager accountManager = (AccountManager) this.getSystemService(ACCOUNT_SERVICE);
if(accountManager.addAccountExplicitly(newAccount, null, null)){
         ContentResolver.setIsSyncable(account, AUTHORITY, 1);
         ContentResolver.setSyncAutomatically(account, AUTHORITY, true);
}

Then you can trigger sync anytime. It will trigger.

Note: onPerformSync() of setSyncAutomatically() will be called first then only other sync request will be triggered. But to make force sync just add 2 extra flags.

Bundle bundle= new Bundle();
    bundle.putBoolean(
            ContentResolver.SYNC_EXTRAS_MANUAL, true);
    bundle.putBoolean(
            ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
    ContentResolver.requestSync(account, AUTHORITY, bundle);
Share:
14,142
Dandre Allison
Author by

Dandre Allison

Medium: @dandre.allison Twitter: @imminenttweet Instagram: @imminentdomain

Updated on July 03, 2022

Comments

  • Dandre Allison
    Dandre Allison almost 2 years

    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.

  • Dandre Allison
    Dandre Allison almost 12 years
    Adding ContentResolver.setSyncAutomatically(account, AUTHORITY, true); is what I needed, but it seems that it isn't acknowledging the periodic sync. After I add an account it just keeps syncing repeatedly.
  • Blehi
    Blehi almost 12 years
    Please try to call the ContentResolver.addPeriodicSync() function with new Bundle() instead of extras.
  • Dandre Allison
    Dandre Allison almost 12 years
    extras is a new Bundle(), I'm even trying it without attempting to set any of the flags (Bundle extras = new Bundle(); addPeriodicSync(account, AUTHORITY, extras, 900);)
  • kapeels
    kapeels over 10 years
    This is quite late, but what is AUTHORITY here?
  • kapeels
    kapeels over 10 years
    I just figured out it's the string you have in the android:contentAuthority attribute in your sync-adapter tag.