Android SyncAdapter Automatically Initialize Syncing
Asked Answered
C

4

15

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.

Clodhopper answered 18/6, 2012 at 20:32 Comment(0)
C
30
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)
Clodhopper answered 19/6, 2012 at 18:24 Comment(0)
L
14

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.

Lymphosarcoma answered 5/12, 2013 at 11:38 Comment(0)
V
6

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.

Vorticella answered 18/6, 2012 at 20:52 Comment(5)
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.Clodhopper
Please try to call the ContentResolver.addPeriodicSync() function with new Bundle() instead of extras.Vorticella
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);)Clodhopper
This is quite late, but what is AUTHORITY here?Adelinaadelind
I just figured out it's the string you have in the android:contentAuthority attribute in your sync-adapter tag.Adelinaadelind
G
2

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);
Gilcrest answered 23/6, 2015 at 11:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.