Search code examples
javaandroidandroid-syncadapter

SyncAdapter onPerformSync doesn't always get called


I built a SyncAdapter for my app so that I could use Google Cloud Messaging to trigger a sync of database to the server. I am using Volley to actually make the network calls and sync the data, but from what I read when wanting to sync your app you should have a SyncAdapter

My issue is that the onPerformSync() doesn't always run. I will fire the GCM and I always get a log stating that it got through the GCM properly, but my log for the onPerformSync() doesn't always fire. Because it does sometimes I would imagine it is set up properly. But I cannot figure out what is happening when it doesn't

onPerformSync()

@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
    Log.d("onPerformSync", "got to the sync");
}

onMessageReceived() in GCM message handler

@Override
public void onMessageReceived(String from, Bundle data) {

    if (from.equals("/topics/global")) {
        Log.d("gcm topics", data.getString("message"));
        try {
            if (data.getString("message").equals("update")) {
                Log.d("is update", "is message update");
                Account newAccount = new Account(ACCOUNT, ACCOUNT_TYPE);
                ContentResolver.requestSync(newAccount, AUTHORITY, data);
            }
        } catch (NullPointerException e) {
            Log.e("GCM", e.toString());
        }

    } else {
        String message = data.getString("message");
        createNotification(from, message);
    }
}

Creating the account in MainActivity

public static Account createSyncAccount(Context context) {
    // Create the account type and default account
    Account newAccount = new Account(
            ACCOUNT, ACCOUNT_TYPE);
    // Get an instance of the Android account manager
    AccountManager accountManager =
            (AccountManager) context.getSystemService(
                    ACCOUNT_SERVICE);
    /*
     * Add the account and account type, no password or user data
     * If successful, return the Account object, otherwise report an error.
     */
    if (accountManager.addAccountExplicitly(newAccount, null, null)) {
        /*
         * If you don't set android:syncable="true" in
         * in your <provider> element in the manifest,
         * then call context.setIsSyncable(account, AUTHORITY, 1)
         * here.
         */
        ContentResolver.setIsSyncable(newAccount, ArmyContract.CONTENT_AUTHORITY, 1);
        ContentResolver.setSyncAutomatically(newAccount, ArmyContract.CONTENT_AUTHORITY, true);

        return newAccount;
    } else {
        /*
         * The account exists or some other error occurred. Log this, report it,
         * or handle it internally.
         */
        Log.e("Account Creation", "Error withou dummy accocunt");
        return null;
    }
}

syncadapter.xml

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

authenticator.xml

<?xml version="1.0" encoding="utf-8"?>
<account-authenticator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="clashtoolkit.com"
    android:icon="@mipmap/ic_launcher"
    android:smallIcon="@mipmap/ic_launcher"
    android:label="@string/app_name"/>

AndroidManifext.xml

<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission android:name="com.clashtoolkit.clashtoolkit.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />
<uses-permission android:name="com.clashtoolkit.clashtoolkit.permission.C2D_MESSAGE" />
<service
        android:name="com.clashtoolkit.clashtoolkit.network.AuthenticatorService">
        <intent-filter>
            <action android:name="android.accounts.AccountAuthenticator"/>
        </intent-filter>
        <meta-data
            android:name="android.accounts.AccountAuthenticator"
            android:resource="@xml/authenticator" />
    </service>
    <service
        android:name="com.clashtoolkit.clashtoolkit.network.SyncService"
        android:exported="true"
        android:process=":sync">
        <intent-filter>
            <action android:name="android.content.SyncAdapter"/>
        </intent-filter>
        <meta-data android:name="android.content.SyncAdapter"
            android:resource="@xml/syncadapter" />
    </service>

Solution

  • The problem might be in adding these keys to Bundle data :

    // Disable sync backoff and ignore sync preferences. In other words...perform sync NOW!
            data.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
            data.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
            ContentResolver.requestSync(newAccount, AUTHORITY, data);