Tosin Onikute
Tosin Onikute

Reputation: 4002

Sync Adapter not running

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.

Upvotes: 1

Views: 2449

Answers (2)

jee
jee

Reputation: 524

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.

Upvotes: 1

pdegand59
pdegand59

Reputation: 13029

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.

Upvotes: 0

Related Questions