| page.title=Creating a Sync Adapter |
| |
| trainingnavtop=true |
| @jd:body |
| |
| <div id="tb-wrapper"> |
| <div id="tb"> |
| |
| <h2>This lesson teaches you to</h2> |
| <ol> |
| <li> |
| <a href="#CreateSyncAdapter" |
| >Create the Sync Adapter Class</a> |
| </li> |
| <li> |
| <a href="#CreateSyncAdapterService">Bind the Sync Adapter to the Framework</a> |
| </li> |
| <li> |
| <a href="#CreateAccountTypeAccount" |
| >Add the Account Required by the Framework</a> |
| </li> |
| <li> |
| <a href="#CreateSyncAdapterMetadata">Add the Sync Adapter Metadata File</a> |
| </li> |
| <li> |
| <a href="#DeclareSyncAdapterManifest">Declare the Sync Adapter in the Manifest</a> |
| </li> |
| </ol> |
| |
| <h2>You should also read</h2> |
| <ul> |
| <li> |
| <a href="{@docRoot}guide/components/bound-services.html">Bound Services</a> |
| </li> |
| <li> |
| <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> |
| </li> |
| <li> |
| <a href="{@docRoot}training/id-auth/custom_auth.html">Creating a Custom Account Type</a> |
| </li> |
| </ul> |
| |
| <h2>Try it out</h2> |
| |
| <div class="download-box"> |
| <a href="http://developer.android.com/shareables/training/BasicSyncAdapter.zip" class="button">Download the sample</a> |
| <p class="filename">BasicSyncAdapter.zip</p> |
| </div> |
| |
| </div> |
| </div> |
| <p> |
| The sync adapter component in your app encapsulates the code for the tasks that transfer |
| data between the device and a server. Based on the scheduling and triggers you provide in |
| your app, the sync adapter framework runs the code in the sync adapter component. To add a |
| sync adapter component to your app, you need to add the following pieces: |
| <dl> |
| <dt> |
| Sync adapter class. |
| </dt> |
| <dd> |
| A class that wraps your data transfer code in an interface compatible with the sync adapter |
| framework. |
| </dd> |
| <dt> |
| Bound {@link android.app.Service}. |
| </dt> |
| <dd> |
| A component that allows the sync adapter framework to run the code in your sync adapter |
| class. |
| </dd> |
| <dt> |
| Sync adapter XML metadata file. |
| </dt> |
| <dd> |
| A file containing information about your sync adapter. The framework reads this file to |
| find out how to load and schedule your data transfer. |
| </dd> |
| <dt> |
| Declarations in the app manifest. |
| </dt> |
| <dd> |
| XML that declares the bound service and points to sync adapter-specific metadata. |
| </dd> |
| </dl> |
| <p> |
| This lesson shows you how to define these elements. |
| </p> |
| <h2 id="CreateSyncAdapter">Create a Sync Adapter Class</h2> |
| <p> |
| In this part of the lesson you learn how to create the sync adapter class that encapsulates the |
| data transfer code. Creating the class includes extending the sync adapter base class, defining |
| constructors for the class, and implementing the method where you define the data transfer |
| tasks. |
| </p> |
| <h3>Extend the base sync adapter class AbstractThreadedSyncAdapter</h3> |
| <p> |
| To create the sync adapter component, start by extending |
| {@link android.content.AbstractThreadedSyncAdapter} and writing its constructors. Use the |
| constructors to run setup tasks each time your sync adapter component is created from |
| scratch, just as you use {@link android.app.Activity#onCreate Activity.onCreate()} to set up an |
| activity. For example, if your app uses a content provider to store data, use the constructors |
| to get a {@link android.content.ContentResolver} instance. Since a second form of the |
| constructor was added in Android platform version 3.0 to support the {@code parallelSyncs} |
| argument, you need to create two forms of the constructor to maintain compatibility. |
| </p> |
| <p class="note"> |
| <strong>Note:</strong> The sync adapter framework is designed to work with sync adapter |
| components that are singleton instances. Instantiating the sync adapter component is covered |
| in more detail in the section |
| <a href="#CreateSyncAdapterService">Bind the Sync Adapter to the Framework</a>. |
| </p> |
| <p> |
| The following example shows you how to implement |
| {@link android.content.AbstractThreadedSyncAdapter}and its constructors: |
| </p> |
| <pre style="clear: right"> |
| /** |
| * Handle the transfer of data between a server and an |
| * app, using the Android sync adapter framework. |
| */ |
| 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(); |
| ... |
| } |
| </pre> |
| <h3>Add the data transfer code to onPerformSync()</h3> |
| <p> |
| The sync adapter component does not automatically do data transfer. Instead, it |
| encapsulates your data transfer code, so that the sync adapter framework can run the |
| data transfer in the background, without involvement from your app. When the framework is ready |
| to sync your application's data, it invokes your implementation of the method |
| {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()}. |
| </p> |
| <p> |
| To facilitate the transfer of data from your main app code to the sync adapter component, |
| the sync adapter framework calls |
| {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} with the |
| following arguments: |
| </p> |
| <dl> |
| <dt> |
| Account |
| </dt> |
| <dd> |
| An {@link android.accounts.Account} object associated with the event that triggered |
| the sync adapter. If your server doesn't use accounts, you don't need to use the |
| information in this object. |
| </dd> |
| <dt> |
| Extras |
| </dt> |
| <dd> |
| A {@link android.os.Bundle} containing flags sent by the event that triggered the sync |
| adapter. |
| </dd> |
| <dt> |
| Authority |
| </dt> |
| <dd> |
| The authority of a content provider in the system. Your app has to have access to |
| this provider. Usually, the authority corresponds to a content provider in your own app. |
| </dd> |
| <dt> |
| Content provider client |
| </dt> |
| <dd> |
| A {@link android.content.ContentProviderClient} for the content provider pointed to by the |
| authority argument. A {@link android.content.ContentProviderClient} is a lightweight public |
| interface to a content provider. It has the same basic functionality as a |
| {@link android.content.ContentResolver}. If you're using a content provider to store data |
| for your app, you can connect to the provider with this object. Otherwise, you can ignore |
| it. |
| </dd> |
| <dt> |
| Sync result |
| </dt> |
| <dd> |
| A {@link android.content.SyncResult} object that you use to send information to the sync |
| adapter framework. |
| </dd> |
| </dl> |
| <p> |
| The following snippet shows the overall structure of |
| {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()}: |
| </p> |
| <pre> |
| /* |
| * 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) { |
| /* |
| * Put the data transfer code here. |
| */ |
| ... |
| } |
| </pre> |
| <p> |
| While the actual implementation of |
| {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} is specific to |
| your app's data synchronization requirements and server connection protocols, there are a few |
| general tasks your implementation should perform: |
| </p> |
| <dl> |
| <dt> |
| Connecting to a server |
| </dt> |
| <dd> |
| Although you can assume that the network is available when your data transfer starts, the |
| sync adapter framework doesn't automatically connect to a server. |
| </dd> |
| <dt> |
| Downloading and uploading data |
| </dt> |
| <dd> |
| A sync adapter doesn't automate any data transfer tasks. If you want to download |
| data from a server and store it in a content provider, you have to provide the code that |
| requests the data, downloads it, and inserts it in the provider. Similarly, if you want to |
| send data to a server, you have to read it from a file, database, or provider, and send |
| the necessary upload request. You also have to handle network errors that occur while your |
| data transfer is running. |
| </dd> |
| <dt> |
| Handling data conflicts or determining how current the data is |
| </dt> |
| <dd> |
| A sync adapter doesn't automatically handle conflicts between data on the server and data |
| on the device. Also, it doesn't automatically detect if the data on the server is newer than |
| the data on the device, or vice versa. Instead, you have to provide your own algorithms for |
| handling this situation. |
| </dd> |
| <dt> |
| Clean up. |
| </dt> |
| <dd> |
| Always close connections to a server and clean up temp files and caches at the end of |
| your data transfer. |
| </dd> |
| </dl> |
| <p class="note"> |
| <strong>Note:</strong> The sync adapter framework runs |
| {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} on a |
| background thread, so you don't have to set up your own background processing. |
| </p> |
| <p> |
| In addition to your sync-related tasks, you should try to combine your regular |
| network-related tasks and add them to |
| {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()}. |
| By concentrating all of your network tasks in this method, you conserve the battery power that's |
| needed to start and stop the network interfaces. To learn more about making network access more |
| efficient, see the training class <a href="{@docRoot}training/efficient-downloads/index.html" |
| >Transferring Data Without Draining the Battery</a>, which describes several network access |
| tasks you can include in your data transfer code. |
| </p> |
| <h2 id="CreateSyncAdapterService">Bind the Sync Adapter to the Framework</h2> |
| <p> |
| You now have your data transfer code encapsulated in a sync adapter component, but you have |
| to provide the framework with access to your code. To do this, you need to create a bound |
| {@link android.app.Service} that passes a special Android binder object from the sync adapter |
| component to the framework. With this binder object, the framework can invoke the |
| {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} method and |
| pass data to it. |
| </p> |
| <p> |
| Instantiate your sync adapter component as a singleton in the |
| {@link android.app.Service#onCreate onCreate()} method of the service. By instantiating |
| the component in {@link android.app.Service#onCreate onCreate()}, you defer |
| creating it until the service starts, which happens when the framework first tries to run your |
| data transfer. You need to instantiate the component in a thread-safe manner, in case the sync |
| adapter framework queues up multiple executions of your sync adapter in response to triggers or |
| scheduling. |
| </p> |
| <p> |
| For example, the following snippet shows you how to create a class that implements the |
| bound {@link android.app.Service}, instantiates your sync adapter component, and gets the |
| Android binder object: |
| </p> |
| <pre> |
| package com.example.android.syncadapter; |
| /** |
| * Define a Service that returns an {@link android.os.IBinder} for the |
| * sync adapter class, allowing the sync adapter framework to call |
| * onPerformSync(). |
| */ |
| 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(); |
| } |
| } |
| </pre> |
| <p class="note"> |
| <strong>Note:</strong> To see a more detailed example of a bound service for a sync adapter, |
| see the sample app. |
| </p> |
| <h2 id="CreateAccountTypeAccount">Add the Account Required by the Framework</h2> |
| <p> |
| The sync adapter framework requires each sync adapter to have an account type. You declared |
| the account type value in the section |
| <a href="creating-authenticator.html#CreateAuthenticatorFile" |
| >Add the Authenticator Metadata File</a>. Now you have to set up this account type in the |
| Android system. To set up the account type, add a dummy account that uses the account type |
| by calling {@link android.accounts.AccountManager#addAccountExplicitly addAccountExplicitly()}. |
| </p> |
| <p> |
| The best place to call the method is in the |
| {@link android.support.v4.app.FragmentActivity#onCreate onCreate()} method of your app's |
| opening activity. The following code snippet shows you how to do this: |
| </p> |
| <pre> |
| public class MainActivity extends FragmentActivity { |
| ... |
| ... |
| // Constants |
| // The authority for the sync adapter's content provider |
| public static final String AUTHORITY = "com.example.android.datasync.provider"; |
| // An account type, in the form of a domain name |
| public static final String ACCOUNT_TYPE = "example.com"; |
| // The account name |
| public static final String ACCOUNT = "dummyaccount"; |
| // Instance fields |
| Account mAccount; |
| ... |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| ... |
| // Create the dummy account |
| mAccount = CreateSyncAccount(this); |
| ... |
| } |
| ... |
| /** |
| * Create a new dummy account for the sync adapter |
| * |
| * @param context The application context |
| */ |
| 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. |
| */ |
| 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. |
| */ |
| } else { |
| /* |
| * The account exists or some other error occurred. Log this, report it, |
| * or handle it internally. |
| */ |
| } |
| } |
| ... |
| } |
| </pre> |
| <h2 id="CreateSyncAdapterMetadata">Add the Sync Adapter Metadata File</h2> |
| <p> |
| To plug your sync adapter component into the framework, you need to provide the framework |
| with metadata that describes the component and provides additional flags. The metadata specifies |
| the account type you've created for your sync adapter, declares a content provider authority |
| associated with your app, controls a part of the system user interface related to sync adapters, |
| and declares other sync-related flags. Declare this metadata in a special XML file stored in |
| the {@code /res/xml/} directory in your app project. You can give any name to the file, |
| although it's usually called {@code syncadapter.xml}. |
| </p> |
| <p> |
| This XML file contains a single XML element <code><sync-adapter></code> that has the |
| following attributes: |
| </p> |
| <dl> |
| <dt><code>android:contentAuthority</code></dt> |
| <dd> |
| The URI authority for your content provider. If you created a stub content provider for |
| your app in the previous lesson <a href="creating-stub-provider.html" |
| >Creating a Stub Content Provider</a>, use the value you specified for the |
| attribute |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#auth">android:authorities</a></code> |
| in the <code><a href="{@docRoot}guide/topics/manifest/provider-element.html" |
| ><provider></a></code> element you added to your app manifest. This attribute is |
| described in more detail in the section |
| <a href="creating-stub-provider.html#DeclareProvider" |
| >Declare the Provider in the Manifest</a>. |
| <br/> |
| If you're transferring data from a content provider to a server with your sync adapter, this |
| value should be the same as the content URI authority you're using for that data. This value |
| is also one of the authorities you specify in the |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#auth">android:authorities</a></code> |
| attribute of the <code><a href="{@docRoot}guide/topics/manifest/provider-element.html" |
| ><provider></a></code> element that declares your provider in your app manifest. |
| </dd> |
| <dt><code>android:accountType</code></dt> |
| <dd> |
| The account type required by the sync adapter framework. The value must be the same |
| as the account type value you provided when you created the authenticator metadata file, as |
| described in the section <a href="creating-authenticator.html#CreateAuthenticatorFile" |
| >Add the Authenticator Metadata File</a>. It's also the value you specified for the |
| constant {@code ACCOUNT_TYPE} in the code snippet in the section |
| <a href="#CreateAccountTypeAccount">Add the Account Required by the Framework</a>. |
| </dd> |
| <dt>Settings attributes</dt> |
| <dd> |
| <dl> |
| <dt> |
| {@code android:userVisible} |
| </dt> |
| <dd> |
| Sets the visibility of the sync adapter's account type. By default, the |
| account icon and label associated with the account type are visible in the |
| <b>Accounts</b> section of the system's Settings app, so you should make your sync |
| adapter invisible unless you have an account type or domain that's easily associated |
| with your app. If you make your account type invisible, you can still allow users to |
| control your sync adapter with a user interface in one of your app's activities. |
| </dd> |
| <dt> |
| {@code android:supportsUploading} |
| </dt> |
| <dd> |
| Allows you to upload data to the cloud. Set this to {@code false} if your app only |
| downloads data. |
| </dd> |
| <dt> |
| {@code android:allowParallelSyncs} |
| </dt> |
| <dd> |
| Allows multiple instances of your sync adapter component to run at the same time. |
| Use this if your app supports multiple user accounts and you want to allow multiple |
| users to transfer data in parallel. This flag has no effect if you never run |
| multiple data transfers. |
| </dd> |
| <dt> |
| {@code android:isAlwaysSyncable} |
| </dt> |
| <dd> |
| Indicates to the sync adapter framework that it can run your sync adapter at any |
| time you've specified. If you want to programmatically control when your sync |
| adapter can run, set this flag to {@code false}, and then call |
| {@link android.content.ContentResolver#requestSync requestSync()} to run the |
| sync adapter. To learn more about running a sync adapter, see the lesson |
| <a href="running-sync-adapter.html">Running a Sync Adapter</a> |
| </dd> |
| </dl> |
| </dd> |
| </dl> |
| <p> |
| The following example shows the XML for a sync adapter that uses a single dummy account and |
| only does downloads. |
| </p> |
| <pre> |
| <?xml version="1.0" encoding="utf-8"?> |
| <sync-adapter |
| xmlns:android="http://schemas.android.com/apk/res/android" |
| android:contentAuthority="com.example.android.datasync.provider" |
| android:accountType="com.android.example.datasync" |
| android:userVisible="false" |
| android:supportsUploading="false" |
| android:allowParallelSyncs="false" |
| android:isAlwaysSyncable="true"/> |
| </pre> |
| |
| <h2 id="DeclareSyncAdapterManifest">Declare the Sync Adapter in the Manifest</h2> |
| <p> |
| Once you've added the sync adapter component to your app, you have to request permissions |
| related to using the component, and you have to declare the bound {@link android.app.Service} |
| you've added. |
| </p> |
| <p> |
| Since the sync adapter component runs code that transfers data between the network and the |
| device, you need to request permission to access the Internet. In addition, your app needs |
| to request permission to read and write sync adapter settings, so you can control the sync |
| adapter programmatically from other components in your app. You also need to request a |
| special permission that allows your app to use the authenticator component you created |
| in the lesson <a href="creating-authenticator.html">Creating a Stub Authenticator</a>. |
| </p> |
| <p> |
| To request these permissions, add the following to your app manifest as child elements of |
| <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code>: |
| </p> |
| <dl> |
| <dt> |
| {@link android.Manifest.permission#INTERNET android.permission.INTERNET} |
| </dt> |
| <dd> |
| Allows the sync adapter code to access the Internet so that it can download or upload data |
| from the device to a server. You don't need to add this permission again if you were |
| requesting it previously. |
| </dd> |
| <dt> |
| {@link android.Manifest.permission#READ_SYNC_SETTINGS android.permission.READ_SYNC_SETTINGS} |
| </dt> |
| <dd> |
| Allows your app to read the current sync adapter settings. For example, you need this |
| permission in order to call {@link android.content.ContentResolver#getIsSyncable |
| getIsSyncable()}. |
| </dd> |
| <dt> |
| {@link android.Manifest.permission#WRITE_SYNC_SETTINGS android.permission.WRITE_SYNC_SETTINGS} |
| </dt> |
| <dd> |
| Allows your app to control sync adapter settings. You need this permission in order to |
| set periodic sync adapter runs using {@link android.content.ContentResolver#addPeriodicSync |
| addPeriodicSync()}. This permission is <b>not</b> required to call |
| {@link android.content.ContentResolver#requestSync requestSync()}. To learn more about |
| running the sync adapter, see <a href="running-sync-adapter.html" |
| >Running A Sync Adapter</a>. |
| </dd> |
| </dl> |
| <p> |
| The following snippet shows how to add the permissions: |
| </p> |
| <pre> |
| <manifest> |
| ... |
| <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"/> |
| ... |
| </manifest> |
| </pre> |
| <p> |
| Finally, to declare the bound {@link android.app.Service} that the framework uses to |
| interact with your sync adapter, add the following XML to your app manifest as a child element |
| of <code><a href="{@docRoot}guide/topics/manifest/application-element.html" |
| ><application></a></code>: |
| </p> |
| <pre> |
| <service |
| android:name="com.example.android.datasync.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> |
| </pre> |
| <p> |
| The |
| <code><a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"><intent-filter></a></code> |
| element sets up a filter that's triggered by the intent action |
| {@code android.content.SyncAdapter}, sent by the system to run the sync adapter. When the filter |
| is triggered, the system starts the bound service you've created, which in this example is |
| {@code SyncService}. The attribute |
| <code><a href="{@docRoot}guide/topics/manifest/service-element.html#exported">android:exported="true"</a></code> |
| allows processes other than your app (including the system) to access the |
| {@link android.app.Service}. The attribute |
| <code><a href="{@docRoot}guide/topics/manifest/service-element.html#proc">android:process=":sync"</a></code> |
| tells the system to run the {@link android.app.Service} in a global shared process named |
| {@code sync}. If you have multiple sync adapters in your app they can share this process, |
| which reduces overhead. |
| </p> |
| <p> |
| The |
| <code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data></a></code> |
| element provides provides the name of the sync adapter metadata XML file you created previously. |
| The |
| <code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#nm">android:name</a></code> |
| attribute indicates that this metadata is for the sync adapter framework. The |
| <code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#rsrc">android:resource</a></code> |
| element specifies the name of the metadata file. |
| </p> |
| <p> |
| You now have all of the components for your sync adapter. The next lesson shows you how to |
| tell the sync adapter framework to run your sync adapter, either in response to an event or on |
| a regular schedule. |
| </p> |