1

This is my first time creating a Sync adapter, and i'm having issues, I followed the tutorial on the Android Developer site, https://developer.android.com/training/sync-adapters/creating-sync-adapter.html , but i can't seem to get my Sync to work.

I know am doing something wrong, but cannot figure it out myself.

SyncAdapter.

    public class SyncAdapter extends AbstractThreadedSyncAdapter {
        // Global variables
        // Define a variable to contain a content resolver instance
        ContentResolver mContentResolver;

        /**
         * Set up the sync adapter
         */
        public SyncAdapter(Context context, boolean autoInitialize) {
            super(context, autoInitialize);
            /*
             * If your app uses a content resolver, get an instance of it
             * from the incoming Context
             */
            mContentResolver = context.getContentResolver();
        }

        /**
         * Set up the sync adapter. This form of the
         * constructor maintains compatibility with Android 3.0
         * and later platform versions
         */
        public SyncAdapter(
                Context context,
                boolean autoInitialize,
                boolean allowParallelSyncs) {
            super(context, autoInitialize, allowParallelSyncs);
            /*
             * If your app uses a content resolver, get an instance of it
             * from the incoming Context
             */
            mContentResolver = context.getContentResolver();

        }

        public void onPerformSync(
                Account account,
                Bundle extras,
                String authority,
                ContentProviderClient provider,
                SyncResult syncResult) {
        /*
         * Put the data transfer code here.
         */
            Log.d("Message: ", "Perform Sync Call");
            new JSONAsyncTask().execute("http://example.com?category=1");



        }
}

SyncService

public class SyncService extends Service {
    // Storage for an instance of the sync adapter
    private static SyncAdapter sSyncAdapter = null;
    // Object to use as a thread-safe lock
    private static final Object sSyncAdapterLock = new Object();
    /*
     * Instantiate the sync adapter object.
     */
    @Override
    public void onCreate() {
        /*
         * Create the sync adapter as a singleton.
         * Set the sync adapter as syncable
         * Disallow parallel syncs
         */
        synchronized (sSyncAdapterLock) {
            if (sSyncAdapter == null) {
                sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
            }
        }
    }
    /**
     * Return an object that allows the system to invoke
     * the sync adapter.
     *
     */
    @Override
    public IBinder onBind(Intent intent) {
        /*
         * Get the object that allows external processes
         * to call onPerformSync(). The object is created
         * in the base class code when the SyncAdapter
         * constructors call super()
         */
        return sSyncAdapter.getSyncAdapterBinder();
    }
}

my MainActivity

public class MainActivity extends AppCompatActivity{

    int id = 0;
    public static String CONNECTION_STATUS="";
    String TAG ="Message: ";


    /** Sync adapter code **/
    // Constants
    // The authority for the sync adapter's content provider
    public static final String AUTHORITY = "com.example.tech6.sampleapp.contentprovider";
    // An account type, in the form of a domain name
    public static final String ACCOUNT_TYPE = "com.android.example.sampleapp";
    // The account name
    public static final String ACCOUNT = "dummyaccount";
    // Instance fields
    Account mAccount;
    ContentResolver mResolver;


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_movies);

        // Set the menu icon instead of the launcher icon.
        final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);


        ConnectivityManager cm = (ConnectivityManager) getApplicationContext().getSystemService(getApplicationContext().CONNECTIVITY_SERVICE);
        NetworkInfo ni = cm.getActiveNetworkInfo();
        /** Check if connected, then Sync **/
        if (ni != null) {
        Account mAccount = new Account(
                           ACCOUNT, ACCOUNT_TYPE);
        Bundle extras = new Bundle();
        mResolver.setIsSyncable(mAccount, AUTHORITY, 1);
        mResolver.setSyncAutomatically(mAccount, AUTHORITY, true);
        mResolver.requestSync(mAccount, AUTHORITY, extras);
        }



    }

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/Base.Theme.DesignDemo" >
    <activity
        android:name=".activity.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

    <provider
        android:name=".contentprovider.CinemalistingContentProvider"
        android:authorities="com.example.tech6.providersample.contentprovider" >
    </provider>

AndroidManifest.xml

<service
                android:name=".helpers.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/sync_adapter" />
            </service>

        </application>

    </manifest>

xml/sync_adapter.xml

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

Any help is appreciated, thanks.

Tosin Onikute
  • 3,883
  • 6
  • 38
  • 61
  • How did you made conclusion that sync is not working? It will not stop on breakpoint because sync is running in different process. Also log is printed in separate logcat console. You can select process in dropdown list in Android Monitor. – mixel Nov 12 '15 at 18:10
  • For debugging purposes you can remove `android:process=":sync"` from service declaration in AndroidManifest.xml. So sync will run in main process. – mixel Nov 12 '15 at 18:12

2 Answers2

1

First create stub authenticator.

public class Authenticator extends AbstractAccountAuthenticator {
public Authenticator(Context context) {
    super(context);
}

@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
    return null;
}

@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
    return null;
}

@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
    return null;
}

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
    return null;
}

@Override
public String getAuthTokenLabel(String authTokenType) {
    return null;
}

@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
    return null;
}

@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
    return null;
}
}

Bind the authenticator to the sync adapter framework using service .

public class AuthenticatorService extends Service {

// Instance field that stores the authenticator object
private Authenticator mAuthenticator;
@Override
public void onCreate() {
    // Create a new authenticator object
    mAuthenticator = new Authenticator(this);
}

 /*
 * When the system binds to this Service to make the RPC call
 * return the authenticator's IBinder.
 */

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return mAuthenticator.getIBinder();
}
}

3.create a file res/xml/authenticator.xml to add authenticator's metadata file

content of authenticator.xml will be like:

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

Now we need to Create a stub content provider Note:If you already have a content provider in your app, you don't need a stub content provider.Since I don't have any valid content provider, and sync adapter framework needs content provider, so creating a stub content provider.If your app doesn't have a content provider, your sync adapter crashes. 1.Add a stub content provider

public class StubProvider extends ContentProvider{

@Override
public boolean onCreate() {
    return false;
}

@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
    return null;
}

@Nullable
@Override
public String getType(@NonNull Uri uri) {
    return null;
}

@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
    return null;
}

@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
    return 0;
}

@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
    return 0;
}
}

2.Declare the provider in the manifest:

<provider
        android:authorities="app_package.provider"
        android:name=".StubProvider"
        android:exported="false"
        android:syncable="true">

    </provider>

Create a sync adapter 1.create a sync adapter class:

public class SyncAdapter extends AbstractThreadedSyncAdapter {

private static final String TAG = SyncAdapter.class.getSimpleName();
// Global variables
// Define a variable to contain a content resolver instance
ContentResolver mContentResolver;

/**
 * Set up the sync adapter
 */
public SyncAdapter(Context context, boolean autoInitialize) {
    super(context, autoInitialize);
    /*
     * If your app uses a content resolver, get an instance of it
     * from the incoming Context
     */
    mContentResolver = context.getContentResolver();
    Log.d(TAG, "SyncAdapter:  ctr");
}

/**
 * Set up the sync adapter. This form of the
 * constructor maintains compatibility with Android 3.0
 * and later platform versions
 */
public SyncAdapter(
        Context context,
        boolean autoInitialize,
        boolean allowParallelSyncs) {
    super(context, autoInitialize, allowParallelSyncs);
    /*
     * If your app uses a content resolver, get an instance of it
     * from the incoming Context
     */
    mContentResolver = context.getContentResolver();
    Log.d(TAG, "SyncAdapter:  ctr");
}

/*
* Specify the code you want to run in the sync adapter. The entire
* sync adapter runs in a background thread, so you don't have to set
* up your own background processing.
*/
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
                          ContentProviderClient provider, SyncResult syncResult) {
    Log.d(TAG, "onPerformSync:  + called");
   // here write your logic, what exactly you want to sync
}
}

2.Bind the sync adapter to the sync adapter framework using service

public class SyncService extends Service {
// Storage for an instance of the sync adapter
private static SyncAdapter sSyncAdapter = null;
// Object to use as a thread-safe lock
private static final Object sSyncAdapterLock = new Object();

@Override
public void onCreate() {
    /*
     * Create the sync adapter as a singleton.
     * Set the sync adapter as syncable
     * Disallow parallel syncs
     */
    synchronized (sSyncAdapterLock) {
        if (sSyncAdapter == null) {
            sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
        }
    }
}

/**
 * Return an object that allows the system to invoke
 * the sync adapter.
 *
 */
@Nullable
@Override
public IBinder onBind(Intent intent) {
    /*
     * Get the object that allows external processes
     * to call onPerformSync(). The object is created
     * in the base class code when the SyncAdapter
     * constructors call super()
     */
    return sSyncAdapter.getSyncAdapterBinder();
}
}

3.now Add the sync adapter metadata file (res/xml/syncadapter.xml)

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

4.Declare the sync adapter in the manifest

<service
        android:name=".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>

5.Add the account required by the sync adapter framework

public class MainActivity extends FragmentActivity{

   // Constants
// The authority for the sync adapter's content provider
public static final String AUTHORITY = "app_package.provider";
// An account type, in the form of a domain name
public static final String ACCOUNT_TYPE = "client.example.com";
// The account name
public static final String ACCOUNT = "dummy_account";
// Instance fields
public  Account mAccount;
 @Override
protected void onCreate(Bundle savedInstanceState) {
  mAccount = CreateSyncAccount(this);
}


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.
     */
    accountManager.removeAccountExplicitly(newAccount);
    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.
         */
        Log.d(TAG, "*******CreateSyncAccount: successful ");
        ContentResolver.setSyncAutomatically(newAccount, AUTHORITY, true);
    } else {
        /*
         * The account exists or some other error occurred. Log this, report it,
         * or handle it internally.
         */
        Log.d(TAG, "*******CreateSyncAccount: error occured ");
    }
    return newAccount;
}


}

note: Now run your application. Now go to setting, and search account.. click users &accounts. Here your dummy_account should be listed.Click dummy_account->account sync->sync now.

sync adapter's onPerformSync will be called.

Now you should call ContentResolver.requestSync(mAccount, AUTHORITY, new Bundle()); as per your requirement.

jee
  • 524
  • 4
  • 7
0

You have to add the dummy account in the AccountManager in order to register the SyncAdapter in the framework.

Just after your new Account(...), you have to call the following :

    // 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(mAccount, 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. 
         */
        Bundle extras = new Bundle();
        mResolver.setIsSyncable(mAccount, AUTHORITY, 1);
        mResolver.setSyncAutomatically(mAccount, AUTHORITY, true);
        mResolver.requestSync(mAccount, AUTHORITY, extras);
    } else {
        /*
         * The account exists or some other error occurred. Log this, report it,
         * or handle it internally.
         */
    }

Also, don't forget to declare the SyncAdapter in your Manifest with the Sync Adapter MetaData file.

Hope this helps.

pdegand59
  • 12,776
  • 8
  • 51
  • 58
  • thanks, I just did that and now it gives me " caller uid 10059 is different than the authenticator's uid " Exception, I updated my question with my Manifest file and xml/sync_adapter.xml – Tosin Onikute Sep 21 '15 at 13:38
  • Did you defined an Authenticator for your app ? If you didn't, either you register one in your manifest or you can set up one programatically. – pdegand59 Sep 21 '15 at 13:59
  • How can i easily register Authenticator in my manifest quickly ? I just checked out this link https://developer.android.com/training/sync-adapters/creating-authenticator.html#DeclareAuthenticator , and it seems like i have to create an Authenticator Class, AuthenticatorService, authenticator XML, before declaring it in the manifest. – Tosin Onikute Sep 21 '15 at 14:18
  • You can't do it quickly. You indeed have to provide every thing (service and xml). If you want to register a dummy authenticator, you can use an `AccountAuthenticatorActivity` and `setAccountAuthenticatorResult(Bundle result)` method. Check the very last post of this answer : http://stackoverflow.com/questions/8554680/syncadapter-without-an-account – pdegand59 Sep 21 '15 at 14:27
  • Thanks for all your help, I Implemented authenticator fine, And Its only printing " W/AccountManagerService﹕ insertAccountIntoDatabase: Account {name=dummyaccount, type=com.android.example.sampleapp}, skipping since the account already exists " Only. But Sync is not still happening yet. :( – Tosin Onikute Sep 22 '15 at 10:50
  • What happens when you go in Account of the phone settings and force a sync on your dummy account ? – pdegand59 Sep 22 '15 at 11:37
  • It shows me Sample App Account and when i click on it, It Shows me dummyaccount (Sync is On), But Sync doesn't actually work. (thats my issue) – Tosin Onikute Sep 22 '15 at 12:37