Sync Adapter not running

情到浓时终转凉″ 提交于 2019-12-24 14:58:10

问题


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.


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/32694750/sync-adapter-not-running

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!