SyncAdapter onPerformSync() gets called on emulator but NOT when run on physical device

StackOverflow https://stackoverflow.com/questions/22675325

  •  22-06-2023
  •  | 
  •  

Domanda

I have followed Google's tutorial to use a SyncAdapter with a dummyAccount and no ContentProvider.

The tutorial is quite straight forward, and I'm aware of the limitations of using stubs for the Account and ContentProvider. I implemented it and when run on the emulator, it works wonderfully. I see the message in the Log of when it is periodically called with periodicSync(..) and when I request specifically to be called with requestSync(..).

Here is the mind-blowing part, when I run the app on my BQ 5 HD (Android 4.1.2) the SyncAdapter is never ever called. If re-installed the app, but nothing seems to be happening. I've wasted weeks with this, and I can´t figure it out.

Here is the my manifestFile

<?xml version="1.0" encoding="utf-8"?>

<uses-sdk
    android:minSdkVersion="10"
    android:targetSdkVersion="18" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />

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

    ....Whole bunch of activities...

    <provider
        android:name="com.sf.app_name.stubs.StubProvider"
        android:authorities="com.sf.app_name.provider"
        android:exported="false"
        android:syncable="true" />

    <service android:name="com.sf.app_name.stubs.AuthenticatorService" 
               android:exported="false">
        <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.sf.app_name.sync.SyncService"
        android:exported="false"
        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>

</application>

authenticator.xml

    <?xml version="1.0" encoding="utf-8"?>
 <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
                       android:accountType="com.sf.app_name"
                       android:userVisible="false" />

syncadapter.xml

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

Input is very much appreciated.

È stato utile?

Soluzione

Finally figured out what's wrong thanks to ye-old-faithful Log.d(...). It's a definite noob mistake and I'm posting the answer to prevent other beginners from committing the same mistake.

In my code where I create and add the "dummyAccount" needed by the SyncAdapter framework, I did the following:

  Account[] accounts = accountManager.getAccounts();
    if(accounts.length == 0){
        Account newAccount = new Account(ACCOUNT, ACCOUNT_TYPE);
        ContentResolver.setIsSyncable(newAccount, AUTHORITY, 1);
        ContentResolver.setSyncAutomatically(newAccount, AUTHORITY, true);  
        ContentResolver.addPeriodicSync(newAccount, AUTHORITY, new Bundle(), 40l);
        accountManager.addAccountExplicitly(newAccount, null, null);
        Log.d("serverSync", "ContetResolver set periodic sync");
    }

The methodgetAccounts() returns all of the accounts associated to the device. Obviously it is going to return ZERO when run in the emulator because the emulator has no account associated whatsoever. So I thought it save to do it like this. I create my account of type ACCOUNT_TYPE add it, and go merrily along my way. (By the way, the reason for the addPeriodicSync on top of the setIsSyncable and setSyncAutomatially is to solve a problem that pertains to another matter.

Ofcourse, the SyncAdapter of my APP was getting called when the following was executed:

ContentResolver.requestSync(signedInAccountInstance.getDummyAccount(),
                               AccountManagerActivity.AUTHORITY, bundle);

They getDummyAccount(..) was gotten like so:

 Account[] accounts = accountManager.getAccounts();
 return accounts[0];

When this was run on the actual physical device, I should have realized that getAccounts(..) would also return (atleast) the gmail account associated to my Android device. Hence when I would do

ContentResolver.requestSync(signedInAccountInstance.getDummyAccount(),
                               AccountManagerActivity.AUTHORITY, bundle);

the "dummyAccount" sent would be Google's AccountType, and hence why my app's SyncAdapter would never be called. Instead, it was the gmail app's SyncAdapter who was getting called with my bundle. How did I discover this? By printing out my dummyAccount in Log after looking into the Accounts of my physical device under Settings>Accounts, and realizing that for some reason when I executed my app on my mobile, my gmail account would display a "sync error".

All this could have been prevented by doing things correctly the first time:

Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);

Where ACCOUNT_TYPE is static final variable used in my app.

    // An account type, in the form of a domain name
public static final String ACCOUNT_TYPE = "com.sf.app_name";

Hope this helps save someone some headaches.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top