• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Running a Sync Adapter
2
3trainingnavtop=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>
128public 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>
211public 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>
316public 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>
391public 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 SECONDS_PER_MINUTE = 60L;
400    public static final long SYNC_INTERVAL_IN_MINUTES = 60L;
401    public static final long SYNC_INTERVAL =
402            SYNC_INTERVAL_IN_MINUTES *
403            SECONDS_PER_MINUTE;
404    // Global variables
405    // A content resolver for accessing the provider
406    ContentResolver mResolver;
407    ...
408    &#64;Override
409    protected void onCreate(Bundle savedInstanceState) {
410        super.onCreate(savedInstanceState);
411        ...
412        // Get the content resolver for your app
413        mResolver = getContentResolver();
414        /*
415         * Turn on periodic syncing
416         */
417        ContentResolver.addPeriodicSync(
418                ACCOUNT,
419                AUTHORITY,
420                Bundle.EMPTY,
421                SYNC_INTERVAL);
422        ...
423    }
424    ...
425}
426</pre>
427<h2 id="RunOnDemand">Run the Sync Adapter On Demand</h2>
428<p>
429    Running your sync adapter in response to a user request is the least preferable strategy
430    for running a sync adapter. The framework is specifically designed to conserve battery power
431    when it runs sync adapters according to a schedule. Options that run a sync in response to data
432    changes use battery power effectively, since the power is used to provide new data.
433</p>
434<p>
435    In comparison, allowing users to run a sync on demand means that the sync runs by itself, which
436    is inefficient use of network and power resources. Also, providing sync on demand leads users to
437    request a sync even if there's no evidence that the data has changed, and running a sync that
438    doesn't refresh data is an ineffective use of battery power. In general, your app should either
439    use other signals to trigger a sync or schedule them at regular intervals, without user input.
440</p>
441<p>
442    However, if you still want to run the sync adapter on demand, set the sync adapter flags for a
443    manual sync adapter run, then call
444    {@link android.content.ContentResolver#requestSync ContentResolver.requestSync()}.
445</p>
446<p>
447    Run on demand transfers with the following flags:
448</p>
449<dl>
450    <dt>
451        {@link android.content.ContentResolver#SYNC_EXTRAS_MANUAL SYNC_EXTRAS_MANUAL}
452    </dt>
453    <dd>
454        Forces a manual sync. The sync adapter framework ignores the existing settings,
455        such as the flag set by {@link android.content.ContentResolver#setSyncAutomatically
456        setSyncAutomatically()}.
457    </dd>
458    <dt>
459        {@link android.content.ContentResolver#SYNC_EXTRAS_EXPEDITED SYNC_EXTRAS_EXPEDITED}
460    </dt>
461    <dd>
462        Forces the sync to start immediately. If you don't set this, the system may wait several
463        seconds before running the sync request, because it tries to optimize battery use by
464        scheduling many requests in a short period of time.
465    </dd>
466</dl>
467<p>
468    The following code snippet shows you how to call
469    {@link android.content.ContentResolver#requestSync requestSync()} in response to a button
470    click:
471</p>
472<pre>
473public class MainActivity extends FragmentActivity {
474    ...
475    // Constants
476    // Content provider authority
477    public static final String AUTHORITY =
478            "com.example.android.datasync.provider"
479    // Account type
480    public static final String ACCOUNT_TYPE = "com.example.android.datasync";
481    // Account
482    public static final String ACCOUNT = "default_account";
483    // Instance fields
484    Account mAccount;
485    ...
486    &#64;Override
487    protected void onCreate(Bundle savedInstanceState) {
488        super.onCreate(savedInstanceState);
489        ...
490        /*
491         * Create the dummy account. The code for CreateSyncAccount
492         * is listed in the lesson Creating a Sync Adapter
493         */
494
495        mAccount = CreateSyncAccount(this);
496        ...
497    }
498    /**
499     * Respond to a button click by calling requestSync(). This is an
500     * asynchronous operation.
501     *
502     * This method is attached to the refresh button in the layout
503     * XML file
504     *
505     * @param v The View associated with the method call,
506     * in this case a Button
507     */
508    public void onRefreshButtonClick(View v) {
509        ...
510        // Pass the settings flags by inserting them in a bundle
511        Bundle settingsBundle = new Bundle();
512        settingsBundle.putBoolean(
513                ContentResolver.SYNC_EXTRAS_MANUAL, true);
514        settingsBundle.putBoolean(
515                ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
516        /*
517         * Request the sync for the default account, authority, and
518         * manual sync settings
519         */
520        ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);
521    }
522</pre>
523