1page.title=Creating a Sync Adapter 2 3trainingnavtop=true 4@jd:body 5 6<div id="tb-wrapper"> 7<div id="tb"> 8 9<h2>This lesson teaches you to</h2> 10<ol> 11 <li> 12 <a href="#CreateSyncAdapter" 13 >Create the Sync Adapter Class</a> 14 </li> 15 <li> 16 <a href="#CreateSyncAdapterService">Bind the Sync Adapter to the Framework</a> 17 </li> 18 <li> 19 <a href="#CreateAccountTypeAccount" 20 >Add the Account Required by the Framework</a> 21 </li> 22 <li> 23 <a href="#CreateSyncAdapterMetadata">Add the Sync Adapter Metadata File</a> 24 </li> 25 <li> 26 <a href="#DeclareSyncAdapterManifest">Declare the Sync Adapter in the Manifest</a> 27 </li> 28</ol> 29 30<h2>You should also read</h2> 31<ul> 32 <li> 33 <a href="{@docRoot}guide/components/bound-services.html">Bound Services</a> 34 </li> 35 <li> 36 <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> 37 </li> 38 <li> 39 <a href="{@docRoot}training/id-auth/custom_auth.html">Creating a Custom Account Type</a> 40 </li> 41</ul> 42 43<h2>Try it out</h2> 44 45<div class="download-box"> 46 <a href="http://developer.android.com/shareables/training/BasicSyncAdapter.zip" class="button">Download the sample</a> 47 <p class="filename">BasicSyncAdapter.zip</p> 48</div> 49 50</div> 51</div> 52<p> 53 The sync adapter component in your app encapsulates the code for the tasks that transfer 54 data between the device and a server. Based on the scheduling and triggers you provide in 55 your app, the sync adapter framework runs the code in the sync adapter component. To add a 56 sync adapter component to your app, you need to add the following pieces: 57<dl> 58 <dt> 59 Sync adapter class. 60 </dt> 61 <dd> 62 A class that wraps your data transfer code in an interface compatible with the sync adapter 63 framework. 64 </dd> 65 <dt> 66 Bound {@link android.app.Service}. 67 </dt> 68 <dd> 69 A component that allows the sync adapter framework to run the code in your sync adapter 70 class. 71 </dd> 72 <dt> 73 Sync adapter XML metadata file. 74 </dt> 75 <dd> 76 A file containing information about your sync adapter. The framework reads this file to 77 find out how to load and schedule your data transfer. 78 </dd> 79 <dt> 80 Declarations in the app manifest. 81 </dt> 82 <dd> 83 XML that declares the bound service and points to sync adapter-specific metadata. 84 </dd> 85</dl> 86<p> 87 This lesson shows you how to define these elements. 88</p> 89<h2 id="CreateSyncAdapter">Create a Sync Adapter Class</h2> 90<p> 91 In this part of the lesson you learn how to create the sync adapter class that encapsulates the 92 data transfer code. Creating the class includes extending the sync adapter base class, defining 93 constructors for the class, and implementing the method where you define the data transfer 94 tasks. 95</p> 96<h3>Extend the base sync adapter class AbstractThreadedSyncAdapter</h3> 97<p> 98 To create the sync adapter component, start by extending 99 {@link android.content.AbstractThreadedSyncAdapter} and writing its constructors. Use the 100 constructors to run setup tasks each time your sync adapter component is created from 101 scratch, just as you use {@link android.app.Activity#onCreate Activity.onCreate()} to set up an 102 activity. For example, if your app uses a content provider to store data, use the constructors 103 to get a {@link android.content.ContentResolver} instance. Since a second form of the 104 constructor was added in Android platform version 3.0 to support the {@code parallelSyncs} 105 argument, you need to create two forms of the constructor to maintain compatibility. 106</p> 107<p class="note"> 108 <strong>Note:</strong> The sync adapter framework is designed to work with sync adapter 109 components that are singleton instances. Instantiating the sync adapter component is covered 110 in more detail in the section 111 <a href="#CreateSyncAdapterService">Bind the Sync Adapter to the Framework</a>. 112</p> 113<p> 114 The following example shows you how to implement 115 {@link android.content.AbstractThreadedSyncAdapter}and its constructors: 116</p> 117<pre style="clear: right"> 118/** 119 * Handle the transfer of data between a server and an 120 * app, using the Android sync adapter framework. 121 */ 122public class SyncAdapter extends AbstractThreadedSyncAdapter { 123 ... 124 // Global variables 125 // Define a variable to contain a content resolver instance 126 ContentResolver mContentResolver; 127 /** 128 * Set up the sync adapter 129 */ 130 public SyncAdapter(Context context, boolean autoInitialize) { 131 super(context, autoInitialize); 132 /* 133 * If your app uses a content resolver, get an instance of it 134 * from the incoming Context 135 */ 136 mContentResolver = context.getContentResolver(); 137 } 138 ... 139 /** 140 * Set up the sync adapter. This form of the 141 * constructor maintains compatibility with Android 3.0 142 * and later platform versions 143 */ 144 public SyncAdapter( 145 Context context, 146 boolean autoInitialize, 147 boolean allowParallelSyncs) { 148 super(context, autoInitialize, allowParallelSyncs); 149 /* 150 * If your app uses a content resolver, get an instance of it 151 * from the incoming Context 152 */ 153 mContentResolver = context.getContentResolver(); 154 ... 155 } 156</pre> 157<h3>Add the data transfer code to onPerformSync()</h3> 158<p> 159 The sync adapter component does not automatically do data transfer. Instead, it 160 encapsulates your data transfer code, so that the sync adapter framework can run the 161 data transfer in the background, without involvement from your app. When the framework is ready 162 to sync your application's data, it invokes your implementation of the method 163 {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()}. 164</p> 165<p> 166 To facilitate the transfer of data from your main app code to the sync adapter component, 167 the sync adapter framework calls 168 {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} with the 169 following arguments: 170</p> 171<dl> 172 <dt> 173 Account 174 </dt> 175 <dd> 176 An {@link android.accounts.Account} object associated with the event that triggered 177 the sync adapter. If your server doesn't use accounts, you don't need to use the 178 information in this object. 179 </dd> 180 <dt> 181 Extras 182 </dt> 183 <dd> 184 A {@link android.os.Bundle} containing flags sent by the event that triggered the sync 185 adapter. 186 </dd> 187 <dt> 188 Authority 189 </dt> 190 <dd> 191 The authority of a content provider in the system. Your app has to have access to 192 this provider. Usually, the authority corresponds to a content provider in your own app. 193 </dd> 194 <dt> 195 Content provider client 196 </dt> 197 <dd> 198 A {@link android.content.ContentProviderClient} for the content provider pointed to by the 199 authority argument. A {@link android.content.ContentProviderClient} is a lightweight public 200 interface to a content provider. It has the same basic functionality as a 201 {@link android.content.ContentResolver}. If you're using a content provider to store data 202 for your app, you can connect to the provider with this object. Otherwise, you can ignore 203 it. 204 </dd> 205 <dt> 206 Sync result 207 </dt> 208 <dd> 209 A {@link android.content.SyncResult} object that you use to send information to the sync 210 adapter framework. 211 </dd> 212</dl> 213<p> 214 The following snippet shows the overall structure of 215 {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()}: 216</p> 217<pre> 218 /* 219 * Specify the code you want to run in the sync adapter. The entire 220 * sync adapter runs in a background thread, so you don't have to set 221 * up your own background processing. 222 */ 223 @Override 224 public void onPerformSync( 225 Account account, 226 Bundle extras, 227 String authority, 228 ContentProviderClient provider, 229 SyncResult syncResult) { 230 /* 231 * Put the data transfer code here. 232 */ 233 ... 234 } 235</pre> 236<p> 237 While the actual implementation of 238 {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} is specific to 239 your app's data synchronization requirements and server connection protocols, there are a few 240 general tasks your implementation should perform: 241</p> 242<dl> 243 <dt> 244 Connecting to a server 245 </dt> 246 <dd> 247 Although you can assume that the network is available when your data transfer starts, the 248 sync adapter framework doesn't automatically connect to a server. 249 </dd> 250 <dt> 251 Downloading and uploading data 252 </dt> 253 <dd> 254 A sync adapter doesn't automate any data transfer tasks. If you want to download 255 data from a server and store it in a content provider, you have to provide the code that 256 requests the data, downloads it, and inserts it in the provider. Similarly, if you want to 257 send data to a server, you have to read it from a file, database, or provider, and send 258 the necessary upload request. You also have to handle network errors that occur while your 259 data transfer is running. 260 </dd> 261 <dt> 262 Handling data conflicts or determining how current the data is 263 </dt> 264 <dd> 265 A sync adapter doesn't automatically handle conflicts between data on the server and data 266 on the device. Also, it doesn't automatically detect if the data on the server is newer than 267 the data on the device, or vice versa. Instead, you have to provide your own algorithms for 268 handling this situation. 269 </dd> 270 <dt> 271 Clean up. 272 </dt> 273 <dd> 274 Always close connections to a server and clean up temp files and caches at the end of 275 your data transfer. 276 </dd> 277</dl> 278<p class="note"> 279 <strong>Note:</strong> The sync adapter framework runs 280 {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} on a 281 background thread, so you don't have to set up your own background processing. 282</p> 283<p> 284 In addition to your sync-related tasks, you should try to combine your regular 285 network-related tasks and add them to 286 {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()}. 287 By concentrating all of your network tasks in this method, you conserve the battery power that's 288 needed to start and stop the network interfaces. To learn more about making network access more 289 efficient, see the training class <a href="{@docRoot}training/efficient-downloads/index.html" 290 >Transferring Data Without Draining the Battery</a>, which describes several network access 291 tasks you can include in your data transfer code. 292</p> 293<h2 id="CreateSyncAdapterService">Bind the Sync Adapter to the Framework</h2> 294<p> 295 You now have your data transfer code encapsulated in a sync adapter component, but you have 296 to provide the framework with access to your code. To do this, you need to create a bound 297 {@link android.app.Service} that passes a special Android binder object from the sync adapter 298 component to the framework. With this binder object, the framework can invoke the 299 {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} method and 300 pass data to it. 301</p> 302<p> 303 Instantiate your sync adapter component as a singleton in the 304 {@link android.app.Service#onCreate onCreate()} method of the service. By instantiating 305 the component in {@link android.app.Service#onCreate onCreate()}, you defer 306 creating it until the service starts, which happens when the framework first tries to run your 307 data transfer. You need to instantiate the component in a thread-safe manner, in case the sync 308 adapter framework queues up multiple executions of your sync adapter in response to triggers or 309 scheduling. 310</p> 311<p> 312 For example, the following snippet shows you how to create a class that implements the 313 bound {@link android.app.Service}, instantiates your sync adapter component, and gets the 314 Android binder object: 315</p> 316<pre> 317package com.example.android.syncadapter; 318/** 319 * Define a Service that returns an {@link android.os.IBinder} for the 320 * sync adapter class, allowing the sync adapter framework to call 321 * onPerformSync(). 322 */ 323public class SyncService extends Service { 324 // Storage for an instance of the sync adapter 325 private static SyncAdapter sSyncAdapter = null; 326 // Object to use as a thread-safe lock 327 private static final Object sSyncAdapterLock = new Object(); 328 /* 329 * Instantiate the sync adapter object. 330 */ 331 @Override 332 public void onCreate() { 333 /* 334 * Create the sync adapter as a singleton. 335 * Set the sync adapter as syncable 336 * Disallow parallel syncs 337 */ 338 synchronized (sSyncAdapterLock) { 339 if (sSyncAdapter == null) { 340 sSyncAdapter = new SyncAdapter(getApplicationContext(), true); 341 } 342 } 343 } 344 /** 345 * Return an object that allows the system to invoke 346 * the sync adapter. 347 * 348 */ 349 @Override 350 public IBinder onBind(Intent intent) { 351 /* 352 * Get the object that allows external processes 353 * to call onPerformSync(). The object is created 354 * in the base class code when the SyncAdapter 355 * constructors call super() 356 */ 357 return sSyncAdapter.getSyncAdapterBinder(); 358 } 359} 360</pre> 361<p class="note"> 362 <strong>Note:</strong> To see a more detailed example of a bound service for a sync adapter, 363 see the sample app. 364</p> 365<h2 id="CreateAccountTypeAccount">Add the Account Required by the Framework</h2> 366<p> 367 The sync adapter framework requires each sync adapter to have an account type. You declared 368 the account type value in the section 369 <a href="creating-authenticator.html#CreateAuthenticatorFile" 370 >Add the Authenticator Metadata File</a>. Now you have to set up this account type in the 371 Android system. To set up the account type, add a dummy account that uses the account type 372 by calling {@link android.accounts.AccountManager#addAccountExplicitly addAccountExplicitly()}. 373</p> 374<p> 375 The best place to call the method is in the 376 {@link android.support.v4.app.FragmentActivity#onCreate onCreate()} method of your app's 377 opening activity. The following code snippet shows you how to do this: 378</p> 379<pre> 380public class MainActivity extends FragmentActivity { 381 ... 382 ... 383 // Constants 384 // The authority for the sync adapter's content provider 385 public static final String AUTHORITY = "com.example.android.datasync.provider" 386 // An account type, in the form of a domain name 387 public static final String ACCOUNT_TYPE = "example.com"; 388 // The account name 389 public static final String ACCOUNT = "dummyaccount"; 390 // Instance fields 391 Account mAccount; 392 ... 393 @Override 394 protected void onCreate(Bundle savedInstanceState) { 395 super.onCreate(savedInstanceState); 396 ... 397 // Create the dummy account 398 mAccount = CreateSyncAccount(this); 399 ... 400 } 401 ... 402 /** 403 * Create a new dummy account for the sync adapter 404 * 405 * @param context The application context 406 */ 407 public static Account CreateSyncAccount(Context context) { 408 // Create the account type and default account 409 Account newAccount = new Account( 410 ACCOUNT, ACCOUNT_TYPE); 411 // Get an instance of the Android account manager 412 AccountManager accountManager = 413 (AccountManager) context.getSystemService( 414 ACCOUNT_SERVICE); 415 /* 416 * Add the account and account type, no password or user data 417 * If successful, return the Account object, otherwise report an error. 418 */ 419 if (accountManager.addAccountExplicitly(newAccount, null, null))) { 420 /* 421 * If you don't set android:syncable="true" in 422 * in your <provider> element in the manifest, 423 * then call context.setIsSyncable(account, AUTHORITY, 1) 424 * here. 425 */ 426 } else { 427 /* 428 * The account exists or some other error occurred. Log this, report it, 429 * or handle it internally. 430 */ 431 } 432 } 433 ... 434} 435</pre> 436<h2 id="CreateSyncAdapterMetadata">Add the Sync Adapter Metadata File</h2> 437<p> 438 To plug your sync adapter component into the framework, you need to provide the framework 439 with metadata that describes the component and provides additional flags. The metadata specifies 440 the account type you've created for your sync adapter, declares a content provider authority 441 associated with your app, controls a part of the system user interface related to sync adapters, 442 and declares other sync-related flags. Declare this metadata in a special XML file stored in 443 the {@code /res/xml/} directory in your app project. You can give any name to the file, 444 although it's usually called {@code syncadapter.xml}. 445</p> 446<p> 447 This XML file contains a single XML element <code><sync-adapter></code> that has the 448 following attributes: 449</p> 450<dl> 451 <dt><code>android:contentAuthority</code></dt> 452 <dd> 453 The URI authority for your content provider. If you created a stub content provider for 454 your app in the previous lesson <a href="creating-stub-provider.html" 455 >Creating a Stub Content Provider</a>, use the value you specified for the 456 attribute 457<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#auth">android:authorities</a></code> 458 in the <code><a href="{@docRoot}guide/topics/manifest/provider-element.html" 459 ><provider></a></code> element you added to your app manifest. This attribute is 460 described in more detail in the section 461 <a href="creating-stub-provider.html#DeclareProvider" 462 >Declare the Provider in the Manifest</a>. 463 <br/> 464 If you're transferring data from a content provider to a server with your sync adapter, this 465 value should be the same as the content URI authority you're using for that data. This value 466 is also one of the authorities you specify in the 467<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#auth">android:authorities</a></code> 468 attribute of the <code><a href="{@docRoot}guide/topics/manifest/provider-element.html" 469 ><provider></a></code> element that declares your provider in your app manifest. 470 </dd> 471 <dt><code>android:accountType</code></dt> 472 <dd> 473 The account type required by the sync adapter framework. The value must be the same 474 as the account type value you provided when you created the authenticator metadata file, as 475 described in the section <a href="creating-authenticator.html#CreateAuthenticatorFile" 476 >Add the Authenticator Metadata File</a>. It's also the value you specified for the 477 constant {@code ACCOUNT_TYPE} in the code snippet in the section 478 <a href="#CreateAccountTypeAccount">Add the Account Required by the Framework</a>. 479 </dd> 480 <dt>Settings attributes</dt> 481 <dd> 482 <dl> 483 <dt> 484 {@code android:userVisible} 485 </dt> 486 <dd> 487 Sets the visibility of the sync adapter's account type. By default, the 488 account icon and label associated with the account type are visible in the 489 <b>Accounts</b> section of the system's Settings app, so you should make your sync 490 adapter invisible unless you have an account type or domain that's easily associated 491 with your app. If you make your account type invisible, you can still allow users to 492 control your sync adapter with a user interface in one of your app's activities. 493 </dd> 494 <dt> 495 {@code android:supportsUploading} 496 </dt> 497 <dd> 498 Allows you to upload data to the cloud. Set this to {@code false} if your app only 499 downloads data. 500 </dd> 501 <dt> 502 {@code android:allowParallelSyncs} 503 </dt> 504 <dd> 505 Allows multiple instances of your sync adapter component to run at the same time. 506 Use this if your app supports multiple user accounts and you want to allow multiple 507 users to transfer data in parallel. This flag has no effect if you never run 508 multiple data transfers. 509 </dd> 510 <dt> 511 {@code android:isAlwaysSyncable} 512 </dt> 513 <dd> 514 Indicates to the sync adapter framework that it can run your sync adapter at any 515 time you've specified. If you want to programmatically control when your sync 516 adapter can run, set this flag to {@code false}, and then call 517 {@link android.content.ContentResolver#requestSync requestSync()} to run the 518 sync adapter. To learn more about running a sync adapter, see the lesson 519 <a href="running-sync-adapter.html">Running a Sync Adapter</a> 520 </dd> 521 </dl> 522 </dd> 523</dl> 524<p> 525 The following example shows the XML for a sync adapter that uses a single dummy account and 526 only does downloads. 527</p> 528<pre> 529<?xml version="1.0" encoding="utf-8"?> 530<sync-adapter 531 xmlns:android="http://schemas.android.com/apk/res/android" 532 android:contentAuthority="com.example.android.datasync.provider" 533 android:accountType="com.android.example.datasync" 534 android:userVisible="false" 535 android:supportsUploading="false" 536 android:allowParallelSyncs="false" 537 android:isAlwaysSyncable="true"/> 538</pre> 539 540<h2 id="DeclareSyncAdapterManifest">Declare the Sync Adapter in the Manifest</h2> 541<p> 542 Once you've added the sync adapter component to your app, you have to request permissions 543 related to using the component, and you have to declare the bound {@link android.app.Service} 544 you've added. 545</p> 546<p> 547 Since the sync adapter component runs code that transfers data between the network and the 548 device, you need to request permission to access the Internet. In addition, your app needs 549 to request permission to read and write sync adapter settings, so you can control the sync 550 adapter programmatically from other components in your app. You also need to request a 551 special permission that allows your app to use the authenticator component you created 552 in the lesson <a href="creating-authenticator.html">Creating a Stub Authenticator</a>. 553</p> 554<p> 555 To request these permissions, add the following to your app manifest as child elements of 556<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code>: 557</p> 558<dl> 559 <dt> 560 {@link android.Manifest.permission#INTERNET android.permission.INTERNET} 561 </dt> 562 <dd> 563 Allows the sync adapter code to access the Internet so that it can download or upload data 564 from the device to a server. You don't need to add this permission again if you were 565 requesting it previously. 566 </dd> 567 <dt> 568{@link android.Manifest.permission#READ_SYNC_SETTINGS android.permission.READ_SYNC_SETTINGS} 569 </dt> 570 <dd> 571 Allows your app to read the current sync adapter settings. For example, you need this 572 permission in order to call {@link android.content.ContentResolver#getIsSyncable 573 getIsSyncable()}. 574 </dd> 575 <dt> 576{@link android.Manifest.permission#WRITE_SYNC_SETTINGS android.permission.WRITE_SYNC_SETTINGS} 577 </dt> 578 <dd> 579 Allows your app to control sync adapter settings. You need this permission in order to 580 set periodic sync adapter runs using {@link android.content.ContentResolver#addPeriodicSync 581 addPeriodicSync()}. This permission is <b>not</b> required to call 582 {@link android.content.ContentResolver#requestSync requestSync()}. To learn more about 583 running the sync adapter, see <a href="running-sync-adapter.html" 584 >Running A Sync Adapter</a>. 585 </dd> 586 <dt> 587{@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS android.permission.AUTHENTICATE_ACCOUNTS} 588 </dt> 589 <dd> 590 Allows you to use the authenticator component you created in the lesson 591 <a href="creating-authenticator.html">Creating a Stub Authenticator</a>. 592 </dd> 593</dl> 594<p> 595 The following snippet shows how to add the permissions: 596</p> 597<pre> 598<manifest> 599... 600 <uses-permission 601 android:name="android.permission.INTERNET"/> 602 <uses-permission 603 android:name="android.permission.READ_SYNC_SETTINGS"/> 604 <uses-permission 605 android:name="android.permission.WRITE_SYNC_SETTINGS"/> 606 <uses-permission 607 android:name="android.permission.AUTHENTICATE_ACCOUNTS"/> 608... 609</manifest> 610</pre> 611<p> 612 Finally, to declare the bound {@link android.app.Service} that the framework uses to 613 interact with your sync adapter, add the following XML to your app manifest as a child element 614 of <code><a href="{@docRoot}guide/topics/manifest/application-element.html" 615 ><application></a></code>: 616</p> 617<pre> 618 <service 619 android:name="com.example.android.datasync.SyncService" 620 android:exported="true" 621 android:process=":sync"> 622 <intent-filter> 623 <action android:name="android.content.SyncAdapter"/> 624 </intent-filter> 625 <meta-data android:name="android.content.SyncAdapter" 626 android:resource="@xml/syncadapter" /> 627 </service> 628</pre> 629<p> 630 The 631<code><a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"><intent-filter></a></code> 632 element sets up a filter that's triggered by the intent action 633 {@code android.content.SyncAdapter}, sent by the system to run the sync adapter. When the filter 634 is triggered, the system starts the bound service you've created, which in this example is 635 {@code SyncService}. The attribute 636<code><a href="{@docRoot}guide/topics/manifest/service-element.html#exported">android:exported="true"</a></code> 637 allows processes other than your app (including the system) to access the 638 {@link android.app.Service}. The attribute 639<code><a href="{@docRoot}guide/topics/manifest/service-element.html#proc">android:process=":sync"</a></code> 640 tells the system to run the {@link android.app.Service} in a global shared process named 641 {@code sync}. If you have multiple sync adapters in your app they can share this process, 642 which reduces overhead. 643</p> 644<p> 645 The 646<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data></a></code> 647 element provides provides the name of the sync adapter metadata XML file you created previously. 648 The 649<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#nm">android:name</a></code> 650 attribute indicates that this metadata is for the sync adapter framework. The 651<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#rsrc">android:resource</a></code> 652 element specifies the name of the metadata file. 653</p> 654<p> 655 You now have all of the components for your sync adapter. The next lesson shows you how to 656 tell the sync adapter framework to run your sync adapter, either in response to an event or on 657 a regular schedule. 658</p> 659