Introduced in Android Level 5. SyncAdapters are meant to keep local data on the device (a cache) in sync with data on the web with the goal of:
The Sync Manager will try to reduce the usage of battery by implementing the big cookie model. The transfer needed for all applications will happen at once. Sync Adapter will maintain a balance between:
by being smart about when to place network calls. See Android - Data Transfer.
The Sync Manager:
An account is required by the framework.
An SyncAdapter (such as mySyncAdapter) must extends AbstractThreadedSyncAdapter. Note the following methods:
public final String LOG_TAG = MySyncAdapter.class.getSimpleName();
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
Log.d(LOG_TAG, "Starting sync");
// Do your sync...
}
/**
* Helper method to have the sync adapter sync immediately
* @param context The context used to access the account service
*/
public static void syncImmediately(Context context) {
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
ContentResolver.requestSync(getSyncAccount(context),
context.getString(R.string.content_authority), bundle);
}
public static void configurePeriodicSync(Context context, int syncInterval, int flexTime) {
Account account = getSyncAccount(context);
String authority = context.getString(R.string.content_authority);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// we can enable inexact timers in our periodic sync
SyncRequest request = new SyncRequest.Builder().
syncPeriodic(syncInterval, flexTime).
setSyncAdapter(account, authority).
setExtras(new Bundle()).build();
ContentResolver.requestSync(request);
} else {
ContentResolver.addPeriodicSync(account,
authority, new Bundle(), syncInterval);
}
}
/**
* Helper method to get the fake account to be used with SyncAdapter, or make a new one
* if the fake account doesn't exist yet. If we make a new account, we call the
* onAccountCreated method so we can initialize things.
*
* @param context The context used to access the account service
* @return a fake account.
*/
public static Account getSyncAccount(Context context) {
// Get an instance of the Android account manager
AccountManager accountManager =
(AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
// Create the account type and default account
Account newAccount = new Account(
context.getString(R.string.app_name), context.getString(R.string.sync_account_type));
// If the password doesn't exist, the account doesn't exist
if ( null == accountManager.getPassword(newAccount) ) {
/*
* Add the account and account type, no password or user data
* If successful, return the Account object, otherwise report an error.
*/
if (!accountManager.addAccountExplicitly(newAccount, "", null)) {
return null;
}
/*
* If you don't set android:syncable="true" in
* in your <provider> element in the manifest,
* then call ContentResolver.setIsSyncable(account, AUTHORITY, 1)
* here.
*/
/* For a periodic synchronization */
onAccountCreated(newAccount, context);
}
return newAccount;
}
Example:
Whenever you request a sync, you need a sync account
Example:
Services provides framework access to the syncAdapter (mySyncAdapter) and the authenticator (myAuthenticator)
Example:
Resources to the res/xml folder
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="@string/sync_account_type"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:smallIcon="@mipmap/ic_launcher" />
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="@string/content_authority"
android:accountType="@string/sync_account_type"
android:userVisible="false"
android:supportsUploading="false"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true" />
<!-- Account type (for sync-adapter authenticator) -->
<!-- The string sync_account_type suggests that the account is specific to the app myApp. -->
<string name="sync_account_type">myApp.example.com</string>
<!-- The content_authority string is the Uri authority for your content provider. -->
<!-- Make sure all references to content_authority match the string you define here. -->
<string name="content_authority">com.example.android.myApp.app</string>
In the AndroidManifest
<service android:name=".sync.myAppNameAuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator"
/>
</service>
<provider
android:authorities="@string/content_authority"
android:name=".data.myAppProvider"
android:exported="false"
android:syncable="true" />
<!-- Permissions required by the sync adapter -->
<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"/>
<service
android:name=".sync.myAppSyncService"
android:exported="true"
>
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
In your activity, puts the code below that calls an helper method of the syncAdapter. See SyncAdapter for its implementation
private void updateMyData() {
mySyncAdapter.syncImmediately(getActivity));
}
In the syncAdapter, when a new account is created, call this helper function.
// Interval at which to sync, in seconds.
// 60 seconds (1 minute) * 180 = 3 hours
public static final int SYNC_INTERVAL = 60 * 180;
public static final int SYNC_FLEXTIME = SYNC_INTERVAL/3;
private static void onAccountCreated(Account newAccount, Context context) {
mySyncAdapter.configurePeriodicSync(context, SYNC_INTERVAL, SYNC_FLEXTIME);
/*
* Without calling setSyncAutomatically, our periodic sync will not be enabled.
*/
ContentResolver.setSyncAutomatically(newAccount, context.getString(R.string.content_authority), true);
/*
* Finally, let's do a sync to get things started
*/
syncImmediately(context);
}
public static void initializeSyncAdapter(Context context) {
getSyncAccount(context);
}
and add a call to this function in the main activity at the end of the onCreate method.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//....
mySyncAdapter.initializeSyncAdapter(this);
}
The code flow:
At this point, the app will sync every 3 hours (if the build version is less than KitKat) or everyone 1 hour (if the build version is greater than or equal to KitKat)
A new account must be created on the phone even if it's a dummy.
Below is the sunshine account from the sunshine app