SyncAdapter periodicsync() not triggering

12,176

Solution 1

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);

Solution 2

Periodic Sync does not trigger in some devices if sync option is not set. I went crazy with this issue with a Samsung Galaxy Grand 2 recently...

enter image description here

Solution 3

I just banged my head against a wall for hours trying to figure out why periodic sync wasn't working. It turns out that the poll frequency needs to be in seconds (literal) not milliseconds, and not seconds in milliseconds. So, for example, if you want it to sync every minute and a half, you would need to call:

            ContentResolver.addPeriodicSync(
                    account,
                    authority,
                    Bundle.EMPTY,
                    90
            );

Also, the bundle passed in cannot be null as it is in the documentation, it will throw a NullPointerException.

Solution 4

A periodic sync can only be run if its auto-sync setting is ON and it’s syncable=true. A better approach to this problem will be to use GCM. The server can send push notification to the device(s) whenever there’s a change the client needs to know about. On the client, we request a sync manually upon receiving such message. It’s more battery efficient and we get updated as soon as our server is, without waiting for the next sync cycle.

Solution 5

Periodic syncing will not work if the device internal storage is almost full, even if all your syncing configurations are set in the right way.I think syncing will stop functioning if 96 or 97% of memory is used.I don't know is their any provision to make it working even if memory is almost full. You can observe the same issue in gmail application in android mobile, where the sync will not work when memory is almost full.You can't receive or send message using android gmail application when device memory is almost full.

Share:
12,176
Admin
Author by

Admin

Updated on June 18, 2022

Comments

  • Admin
    Admin almost 2 years

    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

  • Roel
    Roel almost 9 years
    Is this only on specific Android versions? I got my sync working on all the devices here without a label on the content provider except for some at the client. And I don't want to make a release which fixes nothing.
  • welshk91
    welshk91 almost 9 years
    @DalvikVM It's possible that it works without a label and, if it's working for you, I wouldn't fix what ain't broken. I was working with a specific set of Samsung Android tablets and this is what seemed to get it working.
  • Roel
    Roel almost 9 years
    @wwlshk91: It is broken on some tablets of the client. These are Samsung Galaxy Notes 10.1 running Android 4.1.2. Wil updating these tablets to 4.4 help?
  • welshk91
    welshk91 over 8 years
    @DalvikVM Sorry for the delay. I know the above works with many Samsung models running 4.4 and I tested it successfully on at least one model with 4.3. I was able to go as far back as 4.2, but many of the components of my app needed Jelly Bean or higher, so I didn't go any lower.
  • Roel
    Roel over 8 years
    My problems turned out to have nothing to do with Samsung specific things. It was a (stupid) mistake of my own. I searched for accounts but did not check for the type, so on some random tablets I got a non-google account. See stackoverflow.com/q/31290783/2072569.
  • ErickBergmann
    ErickBergmann over 7 years
    ContentResolver.setSyncAutomatically(mAccount, AUTHORITY, true); is what I was missing. Thanks
  • Harkal
    Harkal over 5 years
    one of the most logical answers