Search code examples
androidandroid-serviceandroid-contentproviderandroid-syncadapterandroid-service-binding

SyncService for synchronizing data between android and server not starting


I am currently trying to synchronize some data with a server using a background service. Using the following sites as guides:

I also scoured StackOverflow and have tried the following solutions:

  • Sync Adapter not running
    • Not possible since I am currently using the account the user logged in with and is added to the account manager
  • Sync Adapter not calling onCreate
    • By starting the service using the intent, I get the onCreate() to run for the service but it does not run onBind and the adapter is still not called when onChange runs ContentResolver.requestSync(...)
    • Also, I already currently use requestSync(ACCOUNT, AUTHORITY, new Bundle()) and I triple checked that the account and authority are the same as what is the account type and authority in the syncadapter.xml
  • ContentResolver.requestSync in Sync Adapter is not working in Android
    • I am currently logging and viewing the LogCat with "No Filters" and can see all my other logs. However, the service logs don't show up which means that my service is not created

I have tried other solutions on StackOverflow as well but the solutions are essentially encapsulated by those listed above. I am wondering if there is something missing where I need to start this service in my code.

SyncService.java:

public class SyncService extends Service {
private static SyncAdapter syncAdapter = null;
private static final Object syncAdapterLock = new Object();

@Override
public void onCreate() {
    super.onCreate();
    Log.d("SyncService", "[DEBUG]       SyncService created...");
    synchronized (syncAdapterLock) {
        if (syncAdapter == null) {
            syncAdapter = new SyncAdapter(getApplicationContext(), true);
        }
    }
}

@Override
public IBinder onBind(Intent intent) {
    Log.d("SyncService", "[DEBUG]      onBind(...) run for SyncAdapter...");
    return syncAdapter.getSyncAdapterBinder();
}
}

SyncAdapter.java:

public class SyncAdapter extends AbstractThreadedSyncAdapter {
private ContentResolver resolver;
private AccountManager am;

public SyncAdapter(Context context, boolean autoInitialize) {
    super(context, autoInitialize);
    resolver = context.getContentResolver();
    am = AccountManager.get(context);
}

// Maintains compatibility with Android >= 3.0 platform versions
public SyncAdapter(Context context , boolean autoInitialize, boolean allowParallelSyncs) {
    super(context, autoInitialize, allowParallelSyncs);
    resolver = context.getContentResolver();
    am = AccountManager.get(context);
}

@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
    Log.d("SyncAdapter", "[DEBUG]       onPerformSync for account[" + account.name + "] called. Syncing....");

    // Connect to server
    // Download and upload data
    // Handle data conflicts and determining if data is current
    // Clean up
}
}

Observer:

public class AlarmObserver extends ContentObserver {
Account account;

public AlarmObserver(Handler handler, Account account) {
    super(handler);
    this.account = account;
}

@Override
public void onChange(boolean selfChange) {
    onChange(selfChange, null);
}

@Override
public void onChange(boolean selfChange, Uri changeUri) {
    Log.d("AlarmObserver", "[DEBUG]     Change to content detected. Account: " + account.name + " Uri: " + changeUri.toString() + " Authority: " + AlarmContract.AUTHORITY);
    ContentResolver.requestSync(account, AlarmContract.AUTHORITY, new Bundle());
}
}

syncadapter.xml:

<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    android:contentAuthority="livotto.smarthomeapi.AlarmProvider"
    android:accountType="@string/default_account_type"
    android:userVisible="true"
    android:supportsUploading="true"
    android:allowParallelSyncs="false"
    android:isAlwaysSyncable="true" />

Activity where I am using the resolver:

        AccountManager am = AccountManager.get(this);
    Account[] accounts = am.getAccounts();

    if (accounts.length > 1) {
        // Dialog should appear asking which account to use
    }
    account = accounts[0];      // for now we take the first account

    resolver = getContentResolver();
    observer = new AlarmObserver(new Handler(), account);
    resolver.registerContentObserver(AlarmContract.CONTENT_URI, true, observer);

Solution

  • I have solved the problem I was getting. There are some flags that will control how the sync adapter runs. These are:

    ContentResolver.SYNC_EXTRAS_MANUAL 
    ContentResolver.SYNC_EXTRAS_EXPEDITED
    

    You can set this with the following:

    Bundle bundle = new Bundle();
    bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
    bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
    
    ContentResolver.requestSync(ACCOUNT, AUTHORITY, bundle);
    

    For more information about what these flags do:

    https://books.google.ca/books?id=30G1DAAAQBAJ&lpg=PT265&ots=f6T3TOqtr_&dq=syncservice%20oncreate%20not%20called%20android&pg=PT267#v=onepage&q=syncservice%20oncreate%20not%20called%20android&f=false