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