Reputation: 1123
My syncadapter works well, except for one thing. After the user installs the application, my app syncs twice. Later, if I manually sync it in "settings" it syncs just once as expected. It's just the very first run of the app that this happens.
Here's the code in my "onCreate" that creates account if not already created and sets up the syncadapter. Any ideas on what I'm doing wrong?
if (accountManager.addAccountExplicitly(appAccount, null, null)) {
ContentResolver.setIsSyncable(appAccount, PROVIDER, 1);
ContentResolver.setSyncAutomatically(appAccount, PROVIDER, true);
Bundle extras = new Bundle();
extras.putBoolean("dummy stuff", true);
ContentResolver.addPeriodicSync(appAccount, PROVIDER, extras, 43200);
}
My desired behavior is for the app to sync once immediately after installation and then periodically as per the "addPeriodicSync" statement.
Upvotes: 9
Views: 8061
Reputation: 106
I observed this behavior as well.
It is correct, that addAccountExplicit() will trigger a system-wide account resync of stale accounts.
However, Zapek's observation about addPeriodic sync or request sync being "immediate" syncs, is not quite correct. Both are just queued. Additionally the following holds for addPeriodicSync():
These periodic syncs honor the "syncAutomatically" and "masterSyncAutomatically" settings. Although these sync are scheduled at the specified frequency, it may take longer for it to actually be started if other syncs are ahead of it in the sync operation queue. This means that the actual start time may drift. (Documentation)
What you experience is described in the training on running sync adapters:
The method addPeriodicSync() doesn't disable setSyncAutomatically(), so you may get multiple sync runs in a relatively short period of time. Also, only a few sync adapter control flags are allowed in a call to addPeriodicSync(); the flags that are not allowed are described in the referenced documentation for addPeriodicSync(). Android Training Sync Adapter
Google's own solution looks like yours, with a lower frequency even (60*60=3600):
if (accountManager.addAccountExplicitly(account, null, null)) {
// Inform the system that this account supports sync
ContentResolver.setIsSyncable(account, CONTENT_AUTHORITY, 1);
// Inform the system that this account is eligible for auto sync when the network is up
ContentResolver.setSyncAutomatically(account, CONTENT_AUTHORITY, true);
// Recommend a schedule for automatic synchronization. The system may modify this based
// on other scheduled syncs and network utilization.
ContentResolver.addPeriodicSync(
account, CONTENT_AUTHORITY, new Bundle(),SYNC_FREQUENCY);
newAccount = true;
}
I propose using the SyncStats in onPerformSync() to actually return some information about your initial sync to the system, so it can schedule more efficiently.
syncResult.stats.numEntries++; // For every dataset
this may not help if the other task is already scheduled - investigating
Additionally one may set up a flag 'isInitialOnPerformSync' (w. sharedPreferences), to cause other tasks to back up.
syncResult.delayUntil = <time>;
I personally am not really fan of creating a fixed no sync timeframe after the initial sync.
As stated in the clarification, the sync will not run immediately with your settings. There is a solution, that will let you sync immediately. This will not influence the sync settings, and will not cause them to backoff, which is why this does not solve your problem, but it has the effect that your user will not have to wait for sync to kick in. Important if you use this to display the main content in your app this way.
Code: Set up a flag for isInitialSync inside your normal app process (which you save e.g. in defaultSharedPreferences). You can even use the Upon the initial completion of the installation or login (if authentication is required) you can invoke an immediate sync as follow.
/**
* Start an asynchronous sync operation immediately. </br>
*
* @param account
*/
public static void requestSyncImmediately(Account account) {
// Disable sync backoff and ignore sync preferences. In other words...perform sync NOW!
Bundle settingsBundle = new Bundle();
settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
// Request sync with settings
ContentResolver.requestSync(account, SyncConstants.CONTENT_AUTHORITY, settingsBundle);
}
Upvotes: 4
Reputation: 486
addAccountExplicitely() will cause a sync for all accounts that have an unknown syncable state (which includes your newly added SyncAdapter).
The problem is that it can take from a few seconds to a few minutes to perform, depending on how many other apps with a SyncAdapter and configured accounts are installed.
addPeriodicSync() (or requestSync()) will perform a sync immediately, which is desirable in the case that the user needs to see data as soon as possible when launching the app.
There's not much you can do, other than making sure your syncs are optimized to be quick in the case that no data between the client and server changed.
Upvotes: 1
Reputation: 901
Are you requesting a sync apart from the addPeriodicSync()?
It should sync by itself when you first add the account. So any extra sync requests would account for the double sync.
If that doesn't help, you can always save in preferences the time of the last sync and check against that every sync, so that you can limit the sync frequency to whatever you want.
Hope that helps!
Upvotes: 2