SyncAdapter periodicsync() not triggering

rpan picture rpan · Mar 26, 2013 · Viewed 12k times · Source

I'm trying ty figure out how the syncAdapter works, I used the sampleSync Adapter as an example/starting point and I based my first test on it. The only difference is that I'm not working with the default contacts provider, but that I need one of my own.

This method is kinda the same as in the sampleSyncAdapter demo (in AccountAuthenticatorActivity), i've just added the periodic sync.

    public void finishLogin(String authToken) {
    Log.i(TAG, "finishLogin()");
    final Account account = new Account(mUsername, "be.company.syncAdapterTest");
    if(mRequestNewAccount) {
        mAccountManager.addAccountExplicitly(account, mPassword, null);
        ContentResolver.setIsSyncable(account, MY_AUTHORITY, 1);

        Bundle params = new Bundle();
        params.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
        params.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false);
        params.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);           
        ContentResolver.addPeriodicSync(account, MY_AUTHORITY, params, 30);
        ContentResolver.setSyncAutomatically(account, MY_AUTHORITY, true);
        ContentResolver.requestSync(account,MY_AUTHORITY,params);
    } else {
        mAccountManager.setPassword(account, mPassword);
    }
    final Intent intent = new Intent();
    intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, "ACCOUNT_TEST");
    intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, "be.company.syncAdapterTest");
    setAccountAuthenticatorResult(intent.getExtras());
    setResult(RESULT_OK, intent);
    finish();
}

In the perfomSync() method i have the following method:

    @Override
public void onPerformSync(Account account, Bundle extras, String authority,
        ContentProviderClient provider, SyncResult syncResult) {
    Log.d(TAG, "onPerformSync() start");
    // Testje
    try {
        final String authToken = mAccountManager.blockingGetAuthToken(account, "be.company.syncAdapterTest", NOTIFY_AUTH_FAILURE);
        Log.d(TAG, SAPNetworkUtilities.getWeek(account, authToken, getRandomDate()));
    } catch (OperationCanceledException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (AuthenticatorException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    Log.d(TAG, "onPerformSync() end");
}

Here I just call a simple SAP webservice and show it in the log. Now I have following two questions:

  1. The SYNC is not started automatically when I add my Account in the settings. I need to go inside the account and check the checkbox to start the sync?
  2. The sync is not triggerd every 30 seconds in this example... Do I need to add something in the perfomSync() method in order to let the system know that the sync is done and that the next run can start?

At this moment i do not write the values in the contentProvider, just because i'm trying to figure out how the sync works in detail.

Currently I'm testing on the Android emulator.

Thanks in advance for your feedback.

Kind regards,

Robin

Answer

welshk91 picture welshk91 · Oct 8, 2014

I was also struggling with periodic syncing with the sync adapter. I could fire my SyncAdapter manually with requestSync, but addPeriodicSync would not fire.

I noticed that in all of the examples, going into Settings->Accounts showed the SyncAdapter's account with a little "sync wheel" (usually green if it's syncing fine; red if it failed to sync recently) along with a "Last Synced" timestamp. My account (a dummy account copied and pasted from the Google Docs) didn't have anything like a sync wheel or timestamp listed.

Further digging exposed what turned out to be the problem: my content provider didn't have a label in it's XML (I was using it previously with no problems, so I skimmed through that part of the documentation). Adding a simple label for my content provider caused it to show up under my account in the Settings, along with a sync wheel and timestamp.

Here's some code taken from my app for inspiration. Hopefully it will help someone, somewhere!

/res/xml/sync_adapter.xml

<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.example.database"
android:allowParallelSyncs="false"
android:contentAuthority="com.example.database.data.provider"
android:isAlwaysSyncable="true"
android:supportsUploading="false"
android:userVisible="true" />

/com/example/database/data/MySyncAdapter

public class MySyncAdapter extends AbstractThreadedSyncAdapter {
    private static final String TAG = MySyncAdapter.class.getSimpleName();
    Context context;

    public MySyncAdapter(Context context, boolean autoInitialize) {
        super(context, autoInitialize);
        this.context = context;
    }

    public MySyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
        super(context, autoInitialize, allowParallelSyncs);
        this.context = context;
    }

    @Override
    public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
        Log.e(TAG, "Performing Sync");
    }
}

AndroidManifest.xml (NEEDS Label for Content Provider to show up in Accounts)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.database">

    <uses-sdk tools:node="replace" />

    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="18" />

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
    <uses-permission android:name="android.permission.READ_SYNC_STATS" />


    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:logo="@drawable/chef_collection_logo_white"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <provider
            android:name="com.example.database.data.MyContentProvider"
            android:authorities="com.example.database.data.provider"
            android:label="my provider"
            android:exported="false"
            android:multiprocess="true"
            android:syncable="true" />

        <activity
            android:name=".app.MainActivity"
            android:label="@string/title_activity_main">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name="com.example.database.data.AuthenticatorService"
            android:exported="true"
            android:process=":auth">
            <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.example.database.data.MySyncAdapterService"
            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/sync_adapter" />
        </service>

    </application>

</manifest>

MainActivity. I call this code after the first-run setup wizard, but you can call it anywhere. This will attempt to sync every 30 seconds (used for testing). Note that the Google Docs for this are currently wrong, as it mentions that it wants milliseconds instead of seconds. Another thing to note is that you cannot pass null as a bundle. Doing so will throw an IllegalArgumentException or something similar.

//Create Account
mAccount = createSyncAccount(this);


//Turn on periodic syncing
ContentResolver resolver = getContentResolver();
resolver.setIsSyncable(mAccount, AUTHORITY, 1);
resolver.setSyncAutomatically(mAccount, AUTHORITY, true);

resolver.addPeriodicSync(
        mAccount,
        AUTHORITY,
        Bundle.EMPTY,
        30);