I am investigating on how to call sync adapters in a precise order.
Indeed, I have several providers for items such as clients and contracts. Syncing one of those means:
So, I have to sync clients and THEN sync contracts. Indeed, if I start syncing contracts first, one of them could refer to a client that has not yet been synced and inserted in the smartphone database.
After having performed some tests, I found that sync requests on different providers are performed at the same time. For example, calling :
ContentResolver.requestSync(account, ClientsProvider.AUTHORITY, syncBundle);
ContentResolver.requestSync(account, ContractsProvider.AUTHORITY, syncBundle);
will result in parallel (thus, unordered) syncs of clients and contrats.
Do someone know how to perform sync requests one after the other or have an idea for solving this issue?
The only solution I have found so far is to use a service and the wait()
and notify()
methods in order for the service to pause between sync calls.
If I have 2 providers named ClientsProvider
and ContractsProvider
, I create and declare 2 SyncAdapters, each one having the following onPerformSync()
method:
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult)
{
boolean callerIsMySyncService = extras.getBoolean(GlobalDataSyncService.EXTRA_CALLER_IS_MY_SYNC_ADAPTER, false);
if (callerIsMySyncService)
{
performSync(account, extras, authority, provider, syncResult);
}
else
{
Intent intent = new Intent(context, MySyncService.class);
intent.putExtra(MySyncService.EXTRA_ACCOUNT, account);
context.startService(intent);
}
}
performSync()
is the method where I put my sync logic. The only difference is the need to call MySyncService.releaseLock()
in order to tell the service that sync is finished:
public void performSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult)
{
try
{
// Sync logic goes there
}
finally
{
MySyncService.releaseLock();
}
}
The service code is:
public class MySyncService extends Service
{
public static String EXTRA_CALLER_IS_MY_SYNC_ADAPTER = "isCaller";
public static String EXTRA_ACCOUNT = "account";
private static Boolean isSyncing = Boolean.FALSE;
private static Object lock = new Object();
public MySyncService() {}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
synchronized (isSyncing) {
Account account = (Account)intent.getParcelableExtra(EXTRA_ACCOUNT);
// Sync only if it not already in progress and if there is an account
if ( ! isSyncing && account != null)
{
isSyncing = Boolean.TRUE;
new Thread(new SyncRunner(account)).start();
}
}
return START_REDELIVER_INTENT;
}
public static void releaseLock()
{
synchronized (lock) {
try
{
lock.notify();
}
catch (IllegalMonitorStateException e)
{
// Log error
}
}
}
@Override
public IBinder onBind(Intent intent)
{
throw new UnsupportedOperationException("Not yet implemented");
}
class SyncRunner implements Runnable
{
private Account account;
public SyncRunner(Account account)
{
this.account = account;
}
public void run()
{
try
{
Bundle syncBundle = new Bundle();
syncBundle.putBoolean(EXTRA_CALLER_IS_MY_SYNC_ADAPTER, true);
ContentResolver.requestSync(account, ClientsProvider.AUTHORITY, syncBundle);
synchronized (lock) {
lock.wait();
}
ContentResolver.requestSync(account, ContractsProvider.AUTHORITY, syncBundle);
synchronized (lock) {
lock.wait();
}
}
catch (InterruptedException e)
{
// Log error
}
finally
{
synchronized (isSyncing) {
isSyncing = Boolean.FALSE;
}
}
}
}
}