1 page.title=Running a Sync Adapter 2 3 trainingnavtop=true 4 @jd:body 5 6 7 <div id="tb-wrapper"> 8 <div id="tb"> 9 10 <h2>This lesson teaches you how to:</h2> 11 <ol> 12 <li><a href="#RunByMessage">Run the Sync Adapter When Server Data Changes</a> 13 <li><a href="#RunDataChange">Run the Sync Adapter When Content Provider Data Changes</a></li> 14 <li><a href="#RunPeriodic">Run the Sync Adapter Periodically</a></li> 15 <li><a href="#RunOnDemand">Run the Sync Adapter On Demand</a></li> 16 </ol> 17 18 19 <h2>You should also read</h2> 20 <ul> 21 <li> 22 <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> 23 </li> 24 </ul> 25 26 <h2>Try it out</h2> 27 28 <div class="download-box"> 29 <a href="http://developer.android.com/shareables/training/BasicSyncAdapter.zip" class="button">Download the sample</a> 30 <p class="filename">BasicSyncAdapter.zip</p> 31 </div> 32 33 </div> 34 </div> 35 <p> 36 In the previous lessons in this class, you learned how to create a sync adapter component that 37 encapsulates data transfer code, and how to add the additional components that allow you to 38 plug the sync adapter into the system. You now have everything you need to install an app that 39 includes a sync adapter, but none of the code you've seen actually runs the sync adapter. 40 </p> 41 <p> 42 You should try to run your sync adapter based on a schedule or as the indirect result of some 43 event. For example, you may want your sync adapter to run on a regular schedule, either after a 44 certain period of time or at a particular time of the day. You may also want to run your sync 45 adapter when there are changes to data stored on the device. You should avoid running your 46 sync adapter as the direct result of a user action, because by doing this you don't get the full 47 benefit of the sync adapter framework's scheduling ability. For example, you should avoid 48 providing a refresh button in your user interface. 49 </p> 50 <p> 51 You have the following options for running your sync adapter: 52 </p> 53 <dl> 54 <dt> 55 When server data changes 56 </dt> 57 <dd> 58 Run the sync adapter in response to a message from a server, indicating that server-based 59 data has changed. This option allows you to refresh data from the server to the device 60 without degrading performance or wasting battery life by polling the server. 61 </dd> 62 <dt>When device data changes</dt> 63 <dd> 64 Run a sync adapter when data changes on the device. This option allows you to send 65 modified data from the device to a server, and is especially useful if you need to ensure 66 that the server always has the latest device data. This option is straightforward to 67 implement if you actually store data in your content provider. If you're using a stub 68 content provider, detecting data changes may be more difficult. 69 </dd> 70 <dt> 71 At regular intervals 72 </dt> 73 <dd> 74 Run a sync adapter after the expiration of an interval you choose, or run it at a certain 75 time every day. 76 </dd> 77 <dt>On demand</dt> 78 <dd> 79 Run the sync adapter in response to a user action. However, to provide the best user 80 experience you should rely primarily on one of the more automated options. By using 81 automated options, you conserve battery and network resources. 82 </dd> 83 </dl> 84 <p> 85 The rest of this lesson describes each of the options in more detail. 86 </p> 87 <h2 id="RunByMessage">Run the Sync Adapter When Server Data Changes</h2> 88 <p> 89 If your app transfers data from a server and the server data changes frequently, you can use 90 a sync adapter to do downloads in response to data changes. To run the sync adapter, have 91 the server send a special message to a {@link android.content.BroadcastReceiver} in your app. 92 In response to this message, call {@link android.content.ContentResolver#requestSync 93 ContentResolver.requestSync()} to signal the sync adapter framework to run your 94 sync adapter. 95 </p> 96 <p> 97 <a href="{@docRoot}google/gcm/index.html">Google Cloud Messaging</a> (GCM) provides both the 98 server and device components you need to make this messaging system work. Using GCM to trigger 99 transfers is more reliable and more efficient than polling servers for status. While polling 100 requires a {@link android.app.Service} that is always active, GCM uses a 101 {@link android.content.BroadcastReceiver} that's activated when a message arrives. While polling 102 at regular intervals uses battery power even if no updates are available, GCM only sends 103 messages when needed. 104 </p> 105 <p class="note"> 106 <strong>Note:</strong> If you use GCM to trigger your sync adapter via a broadcast to all 107 devices where your app is installed, remember that they receive your message at 108 roughly the same time. This situation can cause multiple instance of your sync adapter to run 109 at the same time, causing server and network overload. To avoid this situation for a broadcast 110 to all devices, you should consider deferring the start of the sync adapter for a period 111 that's unique for each device. 112 <p> 113 The following code snippet shows you how to run 114 {@link android.content.ContentResolver#requestSync requestSync()} in response to an 115 incoming GCM message: 116 </p> 117 <pre> 118 public class GcmBroadcastReceiver extends BroadcastReceiver { 119 ... 120 // Constants 121 // Content provider authority 122 public static final String AUTHORITY = "com.example.android.datasync.provider" 123 // Account type 124 public static final String ACCOUNT_TYPE = "com.example.android.datasync"; 125 // Account 126 public static final String ACCOUNT = "default_account"; 127 // Incoming Intent key for extended data 128 public static final String KEY_SYNC_REQUEST = 129 "com.example.android.datasync.KEY_SYNC_REQUEST"; 130 ... 131 @Override 132 public void onReceive(Context context, Intent intent) { 133 // Get a GCM object instance 134 GoogleCloudMessaging gcm = 135 GoogleCloudMessaging.getInstance(context); 136 // Get the type of GCM message 137 String messageType = gcm.getMessageType(intent); 138 /* 139 * Test the message type and examine the message contents. 140 * Since GCM is a general-purpose messaging system, you 141 * may receive normal messages that don't require a sync 142 * adapter run. 143 * The following code tests for a a boolean flag indicating 144 * that the message is requesting a transfer from the device. 145 */ 146 if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType) 147 && 148 intent.getBooleanExtra(KEY_SYNC_REQUEST)) { 149 /* 150 * Signal the framework to run your sync adapter. Assume that 151 * app initialization has already created the account. 152 */ 153 ContentResolver.requestSync(ACCOUNT, AUTHORITY, null); 154 ... 155 } 156 ... 157 } 158 ... 159 } 160 </pre> 161 <h2 id="RunDataChange">Run the Sync Adapter When Content Provider Data Changes</h2> 162 <p> 163 If your app collects data in a content provider, and you want to update the server whenever 164 you update the provider, you can set up your app to run your sync adapter automatically. To do 165 this, you register an observer for the content provider. When data in your content provider 166 changes, the content provider framework calls the observer. In the observer, call 167 {@link android.content.ContentResolver#requestSync requestSync()} to tell the framework to run 168 your sync adapter. 169 </p> 170 <p class="note"> 171 <strong>Note:</strong> If you're using a stub content provider, you don't have any data in 172 the content provider and {@link android.database.ContentObserver#onChange onChange()} is 173 never called. In this case, you have to provide your own mechanism for detecting changes to 174 device data. This mechanism is also responsible for calling 175 {@link android.content.ContentResolver#requestSync requestSync()} when the data changes. 176 </p> 177 <p> 178 To create an observer for your content provider, extend the class 179 {@link android.database.ContentObserver} and implement both forms of its 180 {@link android.database.ContentObserver#onChange onChange()} method. In 181 {@link android.database.ContentObserver#onChange onChange()}, call 182 {@link android.content.ContentResolver#requestSync requestSync()} to start the sync adapter. 183 </p> 184 <p> 185 To register the observer, pass it as an argument in a call to 186 {@link android.content.ContentResolver#registerContentObserver registerContentObserver()}. In 187 this call, you also have to pass in a content URI for the data you want to watch. The content 188 provider framework compares this watch URI to content URIs passed in as arguments to 189 {@link android.content.ContentResolver} methods that modify your provider, such as 190 {@link android.content.ContentResolver#insert ContentResolver.insert()}. If there's a match, your 191 implementation of {@link android.database.ContentObserver#onChange ContentObserver.onChange()} 192 is called. 193 </p> 194 195 <p> 196 The following code snippet shows you how to define a {@link android.database.ContentObserver} 197 that calls {@link android.content.ContentResolver#requestSync requestSync()} when a table 198 changes: 199 </p> 200 <pre> 201 public class MainActivity extends FragmentActivity { 202 ... 203 // Constants 204 // Content provider scheme 205 public static final String SCHEME = "content://"; 206 // Content provider authority 207 public static final String AUTHORITY = "com.example.android.datasync.provider"; 208 // Path for the content provider table 209 public static final String TABLE_PATH = "data_table"; 210 // Account 211 public static final String ACCOUNT = "default_account"; 212 // Global variables 213 // A content URI for the content provider's data table 214 Uri mUri; 215 // A content resolver for accessing the provider 216 ContentResolver mResolver; 217 ... 218 public class TableObserver extends ContentObserver { 219 /* 220 * Define a method that's called when data in the 221 * observed content provider changes. 222 * This method signature is provided for compatibility with 223 * older platforms. 224 */ 225 @Override 226 public void onChange(boolean selfChange) { 227 /* 228 * Invoke the method signature available as of 229 * Android platform version 4.1, with a null URI. 230 */ 231 onChange(selfChange, null); 232 } 233 /* 234 * Define a method that's called when data in the 235 * observed content provider changes. 236 */ 237 @Override 238 public void onChange(boolean selfChange, Uri changeUri) { 239 /* 240 * Ask the framework to run your sync adapter. 241 * To maintain backward compatibility, assume that 242 * changeUri is null. 243 */ 244 ContentResolver.requestSync(ACCOUNT, AUTHORITY, null); 245 } 246 ... 247 } 248 ... 249 @Override 250 protected void onCreate(Bundle savedInstanceState) { 251 super.onCreate(savedInstanceState); 252 ... 253 // Get the content resolver object for your app 254 mResolver = getContentResolver(); 255 // Construct a URI that points to the content provider data table 256 mUri = new Uri.Builder() 257 .scheme(SCHEME) 258 .authority(AUTHORITY) 259 .path(TABLE_PATH) 260 .build(); 261 /* 262 * Create a content observer object. 263 * Its code does not mutate the provider, so set 264 * selfChange to "false" 265 */ 266 TableObserver observer = new TableObserver(false); 267 /* 268 * Register the observer for the data table. The table's path 269 * and any of its subpaths trigger the observer. 270 */ 271 mResolver.registerContentObserver(mUri, true, observer); 272 ... 273 } 274 ... 275 } 276 </pre> 277 <h2 id="RunPeriodic">Run the Sync Adapter Periodically</h2> 278 <p> 279 You can run your sync adapter periodically by setting a period of time to wait between runs, 280 or by running it at certain times of the day, or both. Running your sync adapter 281 periodically allows you to roughly match the update interval of your server. 282 </p> 283 <p> 284 Similarly, you can upload data from the device when your server is relatively idle, by 285 scheduling your sync adapter to run at night. Most users leave their powered on and plugged in 286 at night, so this time is usually available. Moreover, the device is not running other tasks at 287 the same time as your sync adapter. If you take this approach, however, you need to ensure that 288 each device triggers a data transfer at a slightly different time. If all devices run your 289 sync adapter at the same time, you are likely to overload your server and cell provider data 290 networks. 291 </p> 292 <p> 293 In general, periodic runs make sense if your users don't need instant updates, but expect to 294 have regular updates. Periodic runs also make sense if you want to balance the availability of 295 up-to-date data with the efficiency of smaller sync adapter runs that don't over-use device 296 resources. 297 </p> 298 <p> 299 To run your sync adapter at regular intervals, call 300 {@link android.content.ContentResolver#addPeriodicSync addPeriodicSync()}. This schedules your 301 sync adapter to run after a certain amount of time has elapsed. Since the sync adapter framework 302 has to account for other sync adapter executions and tries to maximize battery efficiency, the 303 elapsed time may vary by a few seconds. Also, the framework won't run your sync adapter if the 304 network is not available. 305 </p> 306 <p> 307 Notice that {@link android.content.ContentResolver#addPeriodicSync addPeriodicSync()} doesn't 308 run the sync adapter at a particular time of day. To run your sync adapter at roughly the 309 same time every day, use a repeating alarm as a trigger. Repeating alarms are described in more 310 detail in the reference documentation for {@link android.app.AlarmManager}. If you use the 311 method {@link android.app.AlarmManager#setInexactRepeating setInexactRepeating()} to set 312 time-of-day triggers that have some variation, you should still randomize the start time to 313 ensure that sync adapter runs from different devices are staggered. 314 </p> 315 <p> 316 The method {@link android.content.ContentResolver#addPeriodicSync addPeriodicSync()} doesn't 317 disable {@link android.content.ContentResolver#setSyncAutomatically setSyncAutomatically()}, 318 so you may get multiple sync runs in a relatively short period of time. Also, only a few 319 sync adapter control flags are allowed in a call to 320 {@link android.content.ContentResolver#addPeriodicSync addPeriodicSync()}; the flags that are 321 not allowed are described in the referenced documentation for 322 {@link android.content.ContentResolver#addPeriodicSync addPeriodicSync()}. 323 </p> 324 <p> 325 The following code snippet shows you how to schedule periodic sync adapter runs: 326 </p> 327 <pre> 328 public class MainActivity extends FragmentActivity { 329 ... 330 // Constants 331 // Content provider authority 332 public static final String AUTHORITY = "com.example.android.datasync.provider"; 333 // Account 334 public static final String ACCOUNT = "default_account"; 335 // Sync interval constants 336 public static final long SECONDS_PER_MINUTE = 60L; 337 public static final long SYNC_INTERVAL_IN_MINUTES = 60L; 338 public static final long SYNC_INTERVAL = 339 SYNC_INTERVAL_IN_MINUTES * 340 SECONDS_PER_MINUTE; 341 // Global variables 342 // A content resolver for accessing the provider 343 ContentResolver mResolver; 344 ... 345 @Override 346 protected void onCreate(Bundle savedInstanceState) { 347 super.onCreate(savedInstanceState); 348 ... 349 // Get the content resolver for your app 350 mResolver = getContentResolver(); 351 /* 352 * Turn on periodic syncing 353 */ 354 ContentResolver.addPeriodicSync( 355 ACCOUNT, 356 AUTHORITY, 357 Bundle.EMPTY, 358 SYNC_INTERVAL); 359 ... 360 } 361 ... 362 } 363 </pre> 364 <h2 id="RunOnDemand">Run the Sync Adapter On Demand</h2> 365 <p> 366 Running your sync adapter in response to a user request is the least preferable strategy 367 for running a sync adapter. The framework is specifically designed to conserve battery power 368 when it runs sync adapters according to a schedule. Options that run a sync in response to data 369 changes use battery power effectively, since the power is used to provide new data. 370 </p> 371 <p> 372 In comparison, allowing users to run a sync on demand means that the sync runs by itself, which 373 is inefficient use of network and power resources. Also, providing sync on demand leads users to 374 request a sync even if there's no evidence that the data has changed, and running a sync that 375 doesn't refresh data is an ineffective use of battery power. In general, your app should either 376 use other signals to trigger a sync or schedule them at regular intervals, without user input. 377 </p> 378 <p> 379 However, if you still want to run the sync adapter on demand, set the sync adapter flags for a 380 manual sync adapter run, then call 381 {@link android.content.ContentResolver#requestSync ContentResolver.requestSync()}. 382 </p> 383 <p> 384 Run on demand transfers with the following flags: 385 </p> 386 <dl> 387 <dt> 388 {@link android.content.ContentResolver#SYNC_EXTRAS_MANUAL SYNC_EXTRAS_MANUAL} 389 </dt> 390 <dd> 391 Forces a manual sync. The sync adapter framework ignores the existing settings, 392 such as the flag set by {@link android.content.ContentResolver#setSyncAutomatically 393 setSyncAutomatically()}. 394 </dd> 395 <dt> 396 {@link android.content.ContentResolver#SYNC_EXTRAS_EXPEDITED SYNC_EXTRAS_EXPEDITED} 397 </dt> 398 <dd> 399 Forces the sync to start immediately. If you don't set this, the system may wait several 400 seconds before running the sync request, because it tries to optimize battery use by 401 scheduling many requests in a short period of time. 402 </dd> 403 </dl> 404 <p> 405 The following code snippet shows you how to call 406 {@link android.content.ContentResolver#requestSync requestSync()} in response to a button 407 click: 408 </p> 409 <pre> 410 public class MainActivity extends FragmentActivity { 411 ... 412 // Constants 413 // Content provider authority 414 public static final String AUTHORITY = 415 "com.example.android.datasync.provider" 416 // Account type 417 public static final String ACCOUNT_TYPE = "com.example.android.datasync"; 418 // Account 419 public static final String ACCOUNT = "default_account"; 420 // Instance fields 421 Account mAccount; 422 ... 423 @Override 424 protected void onCreate(Bundle savedInstanceState) { 425 super.onCreate(savedInstanceState); 426 ... 427 /* 428 * Create the dummy account. The code for CreateSyncAccount 429 * is listed in the lesson Creating a Sync Adapter 430 */ 431 432 mAccount = CreateSyncAccount(this); 433 ... 434 } 435 /** 436 * Respond to a button click by calling requestSync(). This is an 437 * asynchronous operation. 438 * 439 * This method is attached to the refresh button in the layout 440 * XML file 441 * 442 * @param v The View associated with the method call, 443 * in this case a Button 444 */ 445 public void onRefreshButtonClick(View v) { 446 ... 447 // Pass the settings flags by inserting them in a bundle 448 Bundle settingsBundle = new Bundle(); 449 settingsBundle.putBoolean( 450 ContentResolver.SYNC_EXTRAS_MANUAL, true); 451 settingsBundle.putBoolean( 452 ContentResolver.SYNC_EXTRAS_EXPEDITED, true); 453 /* 454 * Request the sync for the default account, authority, and 455 * manual sync settings 456 */ 457 ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle); 458 } 459 </pre> 460