问题
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