--- /dev/null
+page.title=Creating a Stub Authenticator
+
+trainingnavtop=true
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li>
+ <a href="#CreateAuthenticator">Add a Stub Authenticator Component</a>
+ </li>
+ <li>
+ <a href="#CreateAuthenticatorService">Bind the Authenticator to the Framework</a>
+ </li>
+ <li>
+ <a href="#CreateAuthenticatorFile">Add the Authenticator Metadata File</a>
+ </li>
+ <li>
+ <a href="#DeclareAuthenticator">Declare the Authenticator in the Manifest</a>
+ </li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li>
+ <a href="{@docRoot}guide/components/bound-services.html">Bound Services</a>
+ </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="http://developer.android.com/shareables/training/BasicSyncAdapter.zip" class="button">Download the sample</a>
+ <p class="filename">BasicSyncAdapter.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+ The sync adapter framework assumes that your sync adapter transfers data between device storage
+ associated with an account and server storage that requires login access. For this reason, the
+ framework expects you to provide a component called an authenticator as part of your sync
+ adapter. This component plugs into the Android accounts and authentication framework and
+ provides a standard interface for handling user credentials such as login information.
+</p>
+<p>
+ Even if your app doesn't use accounts, you still need to provide an authenticator component.
+ If you don't use accounts or server login, the information handled by the authenticator is
+ ignored, so you can provide an authenticator component that contains stub method
+ implementations. You also need to provide a bound {@link android.app.Service} that
+ allows the sync adapter framework to call the authenticator's methods.
+</p>
+<p>
+ This lesson shows you how to define all the parts of a stub authenticator that you need to
+ satisfy the requirements of the sync adapter framework. If you need to provide a real
+ authenticator that handles user accounts, read the reference documentation for
+ {@link android.accounts.AbstractAccountAuthenticator}.
+</p>
+
+<h2 id="CreateAuthenticator">Add a Stub Authenticator Component</h2>
+<p>
+ To add a stub authenticator component to your app, create a class that extends
+ {@link android.accounts.AbstractAccountAuthenticator}, and then stub out the required methods,
+ either by returning {@code null} or by throwing an exception.
+</p>
+<p>
+ The following snippet shows an example of a stub authenticator class:
+</p>
+<pre>
+/*
+ * Implement AbstractAccountAuthenticator and stub out all
+ * of its methods
+ */
+public class Authenticator extends AbstractAccountAuthenticator {
+ // Simple constructor
+ public Authenticator(Context context) {
+ super(context);
+ }
+ // Editing properties is not supported
+ @Override
+ public Bundle editProperties(
+ AccountAuthenticatorResponse r, String s) {
+ throw new UnsupportedOperationException();
+ }
+ // Don't add additional accounts
+ @Override
+ public Bundle addAccount(
+ AccountAuthenticatorResponse r,
+ String s,
+ String s2,
+ String[] strings,
+ Bundle bundle) throws NetworkErrorException {
+ return null;
+ }
+ // Ignore attempts to confirm credentials
+ @Override
+ public Bundle confirmCredentials(
+ AccountAuthenticatorResponse r,
+ Account account,
+ Bundle bundle) throws NetworkErrorException {
+ return null;
+ }
+ // Getting an authentication token is not supported
+ @Override
+ public Bundle getAuthToken(
+ AccountAuthenticatorResponse r,
+ Account account,
+ String s,
+ Bundle bundle) throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+ // Getting a label for the auth token is not supported
+ @Override
+ public String getAuthTokenLabel(String s) {
+ throw new UnsupportedOperationException();
+ }
+ // Updating user credentials is not supported
+ @Override
+ public Bundle updateCredentials(
+ AccountAuthenticatorResponse r,
+ Account account,
+ String s, Bundle bundle) throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+ // Checking features for the account is not supported
+ @Override
+ public Bundle hasFeatures(
+ AccountAuthenticatorResponse r,
+ Account account, String[] strings) throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+}
+</pre>
+<h2 id="CreateAuthenticatorService">Bind the Authenticator to the Framework</h2>
+<p>
+ In order for the sync adapter framework to access your authenticator, you must create a bound
+ Service for it. This service provides an Android binder object that allows the framework
+ to call your authenticator and pass data between the authenticator and the framework.
+</p>
+<p>
+ Since the framework starts this {@link android.app.Service} the first time it needs to
+ access the authenticator, you can also use the service to instantiate the authenticator,
+ by calling the authenticator constructor in the
+ {@link android.app.Service#onCreate Service.onCreate()} method of the service.
+</p>
+<p>
+ The following snippet shows you how to define the bound {@link android.app.Service}:
+</p>
+<pre>
+/**
+ * A bound Service that instantiates the authenticator
+ * when started.
+ */
+public class AuthenticatorService extends Service {
+ ...
+ // Instance field that stores the authenticator object
+ private Authenticator mAuthenticator;
+ @Override
+ public void onCreate() {
+ // Create a new authenticator object
+ mAuthenticator = new Authenticator(this);
+ }
+ /*
+ * When the system binds to this Service to make the RPC call
+ * return the authenticator's IBinder.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mAuthenticator.getIBinder();
+ }
+}
+</pre>
+
+<h2 id="CreateAuthenticatorFile">Add the Authenticator Metadata File</h2>
+<p>
+ To plug your authenticator component into the sync adapter and account frameworks, you need to
+ provide these framework with metadata that describes the component. This metadata declares the
+ account type you've created for your sync adapter and declares user interface elements
+ that the system displays if you want to make your account type visible to the user. Declare this
+ metadata in a XML file stored in the {@code /res/xml/} directory in your app project.
+ You can give any name to the file, although it's usually called {@code authenticator.xml}.
+</p>
+<p>
+ This XML file contains a single element <code><account-authenticator></code> that
+ has the following attributes:
+</p>
+<dl>
+ <dt>
+ <code>android:accountType</code>
+ </dt>
+ <dd>
+ The sync adapter framework requires each sync adapter to have an account type, in the form
+ of a domain name. The framework uses the account type as part of the sync adapter's
+ internal identification. For servers that require login, the account type along with a
+ user account is sent to the server as part of the login credentials.
+ <p>
+ If your server doesn't require login, you still have to provide an account type. For the
+ value, use a domain name that you control. While the framework uses it to manage your
+ sync adapter, the value is not sent to your server.
+ </p>
+ </dd>
+ <dt>
+ <code>android:icon</code>
+ </dt>
+ <dd>
+ Pointer to a <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable</a>
+ resource containing an icon. If you make the sync adapter visible by specifying the
+ attribute <code>android:userVisible="true"</code> in <code>res/xml/syncadapter.xml</code>,
+ then you must provide this icon resource. It appears in the <b>Accounts</b> section of
+ the system's Settings app.
+ </dd>
+ <dt>
+ <code>android:smallIcon</code>
+ </dt>
+ <dd>
+ Pointer to a <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable</a>
+ resource containing a small version of the icon. This resource may be used instead of
+ <code>android:icon</code> in the <b>Accounts</b> section of the system's Settings app,
+ depending on the screen size.
+ </dd>
+ <dt>
+ <code>android:label</code>
+ </dt>
+ <dd>
+ Localizable string that identifies the account type to users. If you make the sync adapter
+ visible by specifying the attribute <code>android:userVisible="true"</code> in
+ <code>res/xml/syncadapter.xml</code>, then you should provide this string. It appears in the
+ <b>Accounts</b> section of the system's Settings app, next to the icon you define for the
+ authenticator.
+ </dd>
+</dl>
+<p>
+ The following snippet shows the XML file for the authenticator you created previously:
+</p>
+<pre>the
+<?xml version="1.0" encoding="utf-8"?>
+<account-authenticator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:accountType="example.com"
+ android:icon="@drawable/ic_launcher"
+ android:smallIcon="@drawable/ic_launcher"
+ android:label="@string/app_name"/>
+</pre>
+
+<h2 id="DeclareAuthenticator">Declare the Authenticator in the Manifest</h2>
+<p>
+ In a previous step, you created a bound {@link android.app.Service} that links the authenticator
+ to the sync adapter framework. To identify this service to the system, declare it in your app
+ manifest by adding the following
+ <code><a href="{@docRoot}guide/topics/manifest/service-element.html"><service></a></code>
+ element as a child element of
+<code><a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code>:
+</p>
+<pre>
+ <service
+ android:name="com.example.android.syncadapter.AuthenticatorService">
+ <intent-filter>
+ <action android:name="android.accounts.AccountAuthenticator"/>
+ </intent-filter>
+ <meta-data
+ android:name="android.accounts.AccountAuthenticator"
+ android:resource="@xml/authenticator" />
+ </service>
+</pre>
+<p>
+ The
+<code><a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"><intent-filter></a></code>
+ element sets up a filter that's triggered by the intent action
+ {@code android.accounts.AccountAuthenticator}, which sent by the system to run the
+ authenticator. When the filter is triggered, the system starts {@code AuthenticatorService},
+ the bound {@link android.app.Service} you have provided to wrap the authenticator.
+</p>
+<p>
+ The
+<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data></a></code>
+ element declares the metadata for the authenticator. The
+<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#nm">android:name</a></code>
+ attribute links the meta-data to the authentication framework. The
+<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#rsrc">android:resource</a></code>
+ element specifies the name of the authenticator metadata file you created previously.
+</p>
+<p>
+ Besides an authenticator, a sync adapter also requires a content provider. If your app doesn't
+ use a content provider already, go to the next lesson to learn how to create a stub content
+ provider; otherwise, go to the lesson <a href="creating-sync-adapter.html"
+ >Creating a Sync Adapter</a>.
+</p>
--- /dev/null
+page.title=Creating a Stub Content Provider
+
+trainingnavtop=true
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li>
+ <a href="#CreateProvider">Add a Stub Content Provider</a>
+ </li>
+ <li>
+ <a href="#DeclareProvider">Declare the Provider in the Manifest</a>
+ </li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li>
+ <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"
+ >Content Provider Basics</a>
+ </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="http://developer.android.com/shareables/training/BasicSyncAdapter.zip" class="button">Download the sample</a>
+ <p class="filename">BasicSyncAdapter.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+ The sync adapter framework is designed to work with device data managed by the flexible and
+ highly secure content provider framework. For this reason, the sync adapter framework expects
+ that an app that uses the framework has already defined a content provider for its local data.
+ If the sync adapter framework tries to run your sync adapter, and your app doesn't have a
+ content provider, your sync adapter crashes.
+</p>
+<p>
+ If you're developing a new app that transfers data from a server to the device, you should
+ strongly consider storing the local data in a content provider. Besides their importance for
+ sync adapters, content providers offer a variety of security benefits and are specifically
+ designed to handle data storage on Android systems. To learn more about creating a content
+ provider, see <a href="{@docRoot}guide/topics/providers/content-provider-creating.html"
+ >Creating a Content Provider</a>.
+</p>
+<p>
+ However, if you're already storing local data in another form, you can still use a sync
+ adapter to handle data transfer. To satisfy the sync adapter framework requirement for a
+ content provider, add a stub content provider to your app. A stub provider implements the
+ content provider class, but all of its required methods return {@code null} or {@code 0}. If you
+ add a stub provider, you can then use a sync adapter to transfer data from any storage
+ mechanism you choose.
+</p>
+<p>
+ If you already have a content provider in your app, you don't need a stub content provider.
+ In that case, you can skip this lesson and proceed to the lesson
+ <a href="creating-sync-adapter.html">Creating a Sync Adapter</a>. If you don't yet have a
+ content provider, this lesson shows you how to add a stub content provider that allows you to
+ plug your sync adapter into the framework.
+</p>
+<h2 id="CreateProvider">Add a Stub Content Provider</h2>
+<p>
+ To create a stub content provider for your app, extend the class
+ {@link android.content.ContentProvider} and stub out its required methods. The following
+ snippet shows you how to create the stub provider:
+</p>
+<pre>
+/*
+ * Define an implementation of ContentProvider that stubs out
+ * all methods
+ */
+public class StubProvider extends ContentProvider {
+ /*
+ * Always return true, indicating that the
+ * provider loaded correctly.
+ */
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+ /*
+ * Return an empty String for MIME type
+ */
+ @Override
+ public String getType() {
+ return new String();
+ }
+ /*
+ * query() always returns no results
+ *
+ */
+ @Override
+ public Cursor query(
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+ /*
+ * insert() always returns null (no URI)
+ */
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+ /*
+ * delete() always returns "no rows affected" (0)
+ */
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+ /*
+ * update() always returns "no rows affected" (0)
+ */
+ public int update(
+ Uri uri,
+ ContentValues values,
+ String selection,
+ String[] selectionArgs) {
+ return 0;
+ }
+}
+</pre>
+<h2 id="DeclareProvider">Declare the Provider in the Manifest</h2>
+<p>
+ The sync adapter framework verifies that your app has a content provider by checking that your
+ app has declared a provider in its app manifest. To declare the stub provider in the
+ manifest, add a <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"
+ ><provider></a></code> element with the following attributes:
+</p>
+<dl>
+ <dt>
+ <code>android:name="com.example.android.datasync.provider.StubProvider"</code>
+ </dt>
+ <dd>
+ Specifies the fully-qualified name of the class that implements the stub content provider.
+ </dd>
+ <dt>
+ <code>android:authorities="com.example.android.datasync.provider"</code>
+ </dt>
+ <dd>
+ A URI authority that identifies the stub content provider. Make this value your app's
+ package name with the string ".provider" appended to it. Even though you're declaring your
+ stub provider to the system, nothing tries to access the provider itself.
+ </dd>
+ <dt>
+ <code>android:exported="false"</code>
+ </dt>
+ <dd>
+ Determines whether other apps can access the content provider. For your stub content
+ provider, set the value to {@code false}, since there's no need to allow other apps to see
+ the provider. This value doesn't affect the interaction between the sync adapter framework
+ and the content provider.
+ </dd>
+ <dt>
+ <code>android:syncable="true"</code>
+ </dt>
+ <dd>
+ Sets a flag that indicates that the provider is syncable. If you set this flag to
+ {@code true}, you don't have to call {@link android.content.ContentResolver#setIsSyncable
+ setIsSyncable()} in your code. The flag allows the sync adapter framework to make data
+ transfers with the content provider, but transfers only occur if you do them explicitly.
+ </dd>
+</dl>
+<p>
+ The following snippet shows you how to add the
+ <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"
+ ><provider></a></code> element to the app manifest:
+</p>
+<pre>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.network.sync.BasicSyncAdapter"
+ android:versionCode="1"
+ android:versionName="1.0" >
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ ...
+ <provider
+ android:name="com.example.android.datasync.provider.StubProvider"
+ android:authorities="com.example.android.datasync.provider"
+ android:export="false"
+ android:syncable="true"/>
+ ...
+ </application>
+</manifest>
+</pre>
+<p>
+ Now that you have created the dependencies required by the sync adapter framework, you can
+ create the component that encapsulates your data transfer code. This component is called a
+ sync adapter. The next lesson shows you how to add this component to your app.
+</p>
--- /dev/null
+page.title=Creating a Sync Adapter
+
+trainingnavtop=true
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li>
+ <a href="#CreateSyncAdapter"
+ >Create the Sync Adapter Class</a>
+ </li>
+ <li>
+ <a href="#CreateSyncAdapterService">Bind the Sync Adapter to the Framework</a>
+ </li>
+ <li>
+ <a href="#CreateAccountTypeAccount"
+ >Add the Account Required by the Framework</a>
+ </li>
+ <li>
+ <a href="#CreateSyncAdapterMetadata">Add the Sync Adapter Metadata File</a>
+ </li>
+ <li>
+ <a href="#DeclareSyncAdapterManifest">Declare the Sync Adapter in the Manifest</a>
+ </li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li>
+ <a href="{@docRoot}guide/components/bound-services.html">Bound Services</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>
+ </li>
+ <li>
+ <a href="{@docRoot}training/id-auth/custom_auth.html">Creating a Custom Account Type</a>
+ </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="http://developer.android.com/shareables/training/BasicSyncAdapter.zip" class="button">Download the sample</a>
+ <p class="filename">BasicSyncAdapter.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+ The sync adapter component in your app encapsulates the code for the tasks that transfer
+ data between the device and a server. Based on the scheduling and triggers you provide in
+ your app, the sync adapter framework runs the code in the sync adapter component. To add a
+ sync adapter component to your app, you need to add the following pieces:
+<dl>
+ <dt>
+ Sync adapter class.
+ </dt>
+ <dd>
+ A class that wraps your data transfer code in an interface compatible with the sync adapter
+ framework.
+ </dd>
+ <dt>
+ Bound {@link android.app.Service}.
+ </dt>
+ <dd>
+ A component that allows the sync adapter framework to run the code in your sync adapter
+ class.
+ </dd>
+ <dt>
+ Sync adapter XML metadata file.
+ </dt>
+ <dd>
+ A file containing information about your sync adapter. The framework reads this file to
+ find out how to load and schedule your data transfer.
+ </dd>
+ <dt>
+ Declarations in the app manifest.
+ </dt>
+ <dd>
+ XML that declares the bound service and points to sync adapter-specific metadata.
+ </dd>
+</dl>
+<p>
+ This lesson shows you how to define these elements.
+</p>
+<h2 id="CreateSyncAdapter">Create a Sync Adapter Class</h2>
+<p>
+ In this part of the lesson you learn how to create the sync adapter class that encapsulates the
+ data transfer code. Creating the class includes extending the sync adapter base class, defining
+ constructors for the class, and implementing the method where you define the data transfer
+ tasks.
+</p>
+<h3>Extend the base sync adapter class AbstractThreadedSyncAdapter</h3>
+<p>
+ To create the sync adapter component, start by extending
+ {@link android.content.AbstractThreadedSyncAdapter} and writing its constructors. Use the
+ constructors to run setup tasks each time your sync adapter component is created from
+ scratch, just as you use {@link android.app.Activity#onCreate Activity.onCreate()} to set up an
+ activity. For example, if your app uses a content provider to store data, use the constructors
+ to get a {@link android.content.ContentResolver} instance. Since a second form of the
+ constructor was added in Android platform version 3.0 to support the {@code parallelSyncs}
+ argument, you need to create two forms of the constructor to maintain compatibility.
+</p>
+<p class="note">
+ <strong>Note:</strong> The sync adapter framework is designed to work with sync adapter
+ components that are singleton instances. Instantiating the sync adapter component is covered
+ in more detail in the section
+ <a href="#CreateSyncAdapterService">Bind the Sync Adapter to the Framework</a>.
+</p>
+<p>
+ The following example shows you how to implement
+ {@link android.content.AbstractThreadedSyncAdapter}and its constructors:
+</p>
+<pre style="clear: right">
+/**
+ * Handle the transfer of data between a server and an
+ * app, using the Android sync adapter framework.
+ */
+public class SyncAdapter extends AbstractThreadedSyncAdapter {
+ ...
+ // Global variables
+ // Define a variable to contain a content resolver instance
+ ContentResolver mContentResolver;
+ /**
+ * Set up the sync adapter
+ */
+ public SyncAdapter(Context context, boolean autoInitialize) {
+ super(context, autoInitialize);
+ /*
+ * If your app uses a content resolver, get an instance of it
+ * from the incoming Context
+ */
+ mContentResolver = context.getContentResolver();
+ }
+ ...
+ /**
+ * Set up the sync adapter. This form of the
+ * constructor maintains compatibility with Android 3.0
+ * and later platform versions
+ */
+ public SyncAdapter(
+ Context context,
+ boolean autoInitialize,
+ boolean allowParallelSyncs) {
+ super(context, autoInitialize, allowParallelSyncs);
+ /*
+ * If your app uses a content resolver, get an instance of it
+ * from the incoming Context
+ */
+ mContentResolver = context.getContentResolver();
+ ...
+ }
+</pre>
+<h3>Add the data transfer code to onPerformSync()</h3>
+<p>
+ The sync adapter component does not automatically do data transfer. Instead, it
+ encapsulates your data transfer code, so that the sync adapter framework can run the
+ data transfer in the background, without involvement from your app. When the framework is ready
+ to sync your application's data, it invokes your implementation of the method
+ {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()}.
+</p>
+<p>
+ To facilitate the transfer of data from your main app code to the sync adapter component,
+ the sync adapter framework calls
+ {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} with the
+ following arguments:
+</p>
+<dl>
+ <dt>
+ Account
+ </dt>
+ <dd>
+ An {@link android.accounts.Account} object associated with the event that triggered
+ the sync adapter. If your server doesn't use accounts, you don't need to use the
+ information in this object.
+ </dd>
+ <dt>
+ Extras
+ </dt>
+ <dd>
+ A {@link android.os.Bundle} containing flags sent by the event that triggered the sync
+ adapter.
+ </dd>
+ <dt>
+ Authority
+ </dt>
+ <dd>
+ The authority of a content provider in the system. Your app has to have access to
+ this provider. Usually, the authority corresponds to a content provider in your own app.
+ </dd>
+ <dt>
+ Content provider client
+ </dt>
+ <dd>
+ A {@link android.content.ContentProviderClient} for the content provider pointed to by the
+ authority argument. A {@link android.content.ContentProviderClient} is a lightweight public
+ interface to a content provider. It has the same basic functionality as a
+ {@link android.content.ContentResolver}. If you're using a content provider to store data
+ for your app, you can connect to the provider with this object. Otherwise, you can ignore
+ it.
+ </dd>
+ <dt>
+ Sync result
+ </dt>
+ <dd>
+ A {@link android.content.SyncResult} object that you use to send information to the sync
+ adapter framework.
+ </dd>
+</dl>
+<p>
+ The following snippet shows the overall structure of
+ {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()}:
+</p>
+<pre>
+ /*
+ * Specify the code you want to run in the sync adapter. The entire
+ * sync adapter runs in a background thread, so you don't have to set
+ * up your own background processing.
+ */
+ @Override
+ public void onPerformSync(
+ Account account,
+ Bundle extras,
+ String authority,
+ ContentProviderClient provider,
+ SyncResult syncResult) {
+ /*
+ * Put the data transfer code here.
+ */
+ ...
+ }
+</pre>
+<p>
+ While the actual implementation of
+ {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} is specific to
+ your app's data synchronization requirements and server connection protocols, there are a few
+ general tasks your implementation should perform:
+</p>
+<dl>
+ <dt>
+ Connecting to a server
+ </dt>
+ <dd>
+ Although you can assume that the network is available when your data transfer starts, the
+ sync adapter framework doesn't automatically connect to a server.
+ </dd>
+ <dt>
+ Downloading and uploading data
+ </dt>
+ <dd>
+ A sync adapter doesn't automate any data transfer tasks. If you want to download
+ data from a server and store it in a content provider, you have to provide the code that
+ requests the data, downloads it, and inserts it in the provider. Similarly, if you want to
+ send data to a server, you have to read it from a file, database, or provider, and send
+ the necessary upload request. You also have to handle network errors that occur while your
+ data transfer is running.
+ </dd>
+ <dt>
+ Handling data conflicts or determining how current the data is
+ </dt>
+ <dd>
+ A sync adapter doesn't automatically handle conflicts between data on the server and data
+ on the device. Also, it doesn't automatically detect if the data on the server is newer than
+ the data on the device, or vice versa. Instead, you have to provide your own algorithms for
+ handling this situation.
+ </dd>
+ <dt>
+ Clean up.
+ </dt>
+ <dd>
+ Always close connections to a server and clean up temp files and caches at the end of
+ your data transfer.
+ </dd>
+</dl>
+<p class="note">
+ <strong>Note:</strong> The sync adapter framework runs
+ {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} on a
+ background thread, so you don't have to set up your own background processing.
+</p>
+<p>
+ In addition to your sync-related tasks, you should try to combine your regular
+ network-related tasks and add them to
+ {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()}.
+ By concentrating all of your network tasks in this method, you conserve the battery power that's
+ needed to start and stop the network interfaces. To learn more about making network access more
+ efficient, see the training class <a href="{@docRoot}training/efficient-downloads/index.html"
+ >Transferring Data Without Draining the Battery</a>, which describes several network access
+ tasks you can include in your data transfer code.
+</p>
+<h2 id="CreateSyncAdapterService">Bind the Sync Adapter to the Framework</h2>
+<p>
+ You now have your data transfer code encapsulated in a sync adapter component, but you have
+ to provide the framework with access to your code. To do this, you need to create a bound
+ {@link android.app.Service} that passes a special Android binder object from the sync adapter
+ component to the framework. With this binder object, the framework can invoke the
+ {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()} method and
+ pass data to it.
+</p>
+<p>
+ Instantiate your sync adapter component as a singleton in the
+ {@link android.app.Service#onCreate onCreate()} method of the service. By instantiating
+ the component in {@link android.app.Service#onCreate onCreate()}, you defer
+ creating it until the service starts, which happens when the framework first tries to run your
+ data transfer. You need to instantiate the component in a thread-safe manner, in case the sync
+ adapter framework queues up multiple executions of your sync adapter in response to triggers or
+ scheduling.
+</p>
+<p>
+ For example, the following snippet shows you how to create a class that implements the
+ bound {@link android.app.Service}, instantiates your sync adapter component, and gets the
+ Android binder object:
+</p>
+<pre>
+package com.example.android.syncadapter;
+/**
+ * Define a Service that returns an {@link android.os.IBinder} for the
+ * sync adapter class, allowing the sync adapter framework to call
+ * onPerformSync().
+ */
+public class SyncService extends Service {
+ // Storage for an instance of the sync adapter
+ private static SyncAdapter sSyncAdapter = null;
+ // Object to use as a thread-safe lock
+ private static final Object sSyncAdapterLock = new Object();
+ /*
+ * Instantiate the sync adapter object.
+ */
+ @Override
+ public void onCreate() {
+ /*
+ * Create the sync adapter as a singleton.
+ * Set the sync adapter as syncable
+ * Disallow parallel syncs
+ */
+ synchronized (sSyncAdapterLock) {
+ if (sSyncAdapter == null) {
+ sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
+ }
+ }
+ }
+ /**
+ * Return an object that allows the system to invoke
+ * the sync adapter.
+ *
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ /*
+ * Get the object that allows external processes
+ * to call onPerformSync(). The object is created
+ * in the base class code when the SyncAdapter
+ * constructors call super()
+ */
+ return sSyncAdapter.getSyncAdapterBinder();
+ }
+}
+</pre>
+<p class="note">
+ <strong>Note:</strong> To see a more detailed example of a bound service for a sync adapter,
+ see the sample app.
+</p>
+<h2 id="CreateAccountTypeAccount">Add the Account Required by the Framework</h2>
+<p>
+ The sync adapter framework requires each sync adapter to have an account type. You declared
+ the account type value in the section
+ <a href="creating-authenticator.html#CreateAuthenticatorFile"
+ >Add the Authenticator Metadata File</a>. Now you have to set up this account type in the
+ Android system. To set up the account type, add a dummy account that uses the account type
+ by calling {@link android.accounts.AccountManager#addAccountExplicitly addAccountExplicitly()}.
+</p>
+<p>
+ The best place to call the method is in the
+ {@link android.support.v4.app.FragmentActivity#onCreate onCreate()} method of your app's
+ opening activity. The following code snippet shows you how to do this:
+</p>
+<pre>
+public class MainActivity extends FragmentActivity {
+ ...
+ ...
+ // Constants
+ // The authority for the sync adapter's content provider
+ public static final String AUTHORITY = "com.example.android.datasync.provider"
+ // An account type, in the form of a domain name
+ public static final String ACCOUNT_TYPE = "example.com";
+ // The account name
+ public static final String ACCOUNT = "dummyaccount";
+ // Instance fields
+ Account mAccount;
+ ...
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ...
+ // Create the dummy account
+ mAccount = CreateSyncAccount(this);
+ ...
+ }
+ ...
+ /**
+ * Create a new dummy account for the sync adapter
+ *
+ * @param context The application context
+ */
+ public static Account CreateSyncAccount(Context context) {
+ // Create the account type and default account
+ Account newAccount = new Account(
+ ACCOUNT, ACCOUNT_TYPE);
+ // Get an instance of the Android account manager
+ AccountManager accountManager =
+ (AccountManager) context.getSystemService(
+ ACCOUNT_SERVICE);
+ /*
+ * Add the account and account type, no password or user data
+ * If successful, return the Account object, otherwise report an error.
+ */
+ if (accountManager.addAccountExplicitly(newAccount, null, null))) {
+ /*
+ * If you don't set android:syncable="true" in
+ * in your <provider> element in the manifest,
+ * then call context.setIsSyncable(account, AUTHORITY, 1)
+ * here.
+ */
+ } else {
+ /*
+ * The account exists or some other error occurred. Log this, report it,
+ * or handle it internally.
+ */
+ }
+ }
+ ...
+}
+</pre>
+<h2 id="CreateSyncAdapterMetadata">Add the Sync Adapter Metadata File</h2>
+<p>
+ To plug your sync adapter component into the framework, you need to provide the framework
+ with metadata that describes the component and provides additional flags. The metadata specifies
+ the account type you've created for your sync adapter, declares a content provider authority
+ associated with your app, controls a part of the system user interface related to sync adapters,
+ and declares other sync-related flags. Declare this metadata in a special XML file stored in
+ the {@code /res/xml/} directory in your app project. You can give any name to the file,
+ although it's usually called {@code syncadapter.xml}.
+</p>
+<p>
+ This XML file contains a single XML element <code><sync-adapter></code> that has the
+ following attributes:
+</p>
+<dl>
+ <dt><code>android:contentAuthority</code></dt>
+ <dd>
+ The URI authority for your content provider. If you created a stub content provider for
+ your app in the previous lesson <a href="creating-stub-provider.html"
+ >Creating a Stub Content Provider</a>, use the value you specified for the
+ attribute
+<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#auth">android:authorities</a></code>
+ in the <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"
+ ><provider></a></code> element you added to your app manifest. This attribute is
+ described in more detail in the section
+ <a href="creating-stub-provider.html#DeclareProvider"
+ >Declare the Provider in the Manifest</a>.
+ <br/>
+ If you're transferring data from a content provider to a server with your sync adapter, this
+ value should be the same as the content URI authority you're using for that data. This value
+ is also one of the authorities you specify in the
+<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#auth">android:authorities</a></code>
+ attribute of the <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"
+ ><provider></a></code> element that declares your provider in your app manifest.
+ </dd>
+ <dt><code>android:accountType</code></dt>
+ <dd>
+ The account type required by the sync adapter framework. The value must be the same
+ as the account type value you provided when you created the authenticator metadata file, as
+ described in the section <a href="creating-authenticator.html#CreateAuthenticatorFile"
+ >Add the Authenticator Metadata File</a>. It's also the value you specified for the
+ constant {@code ACCOUNT_TYPE} in the code snippet in the section
+ <a href="#CreateAccountTypeAccount">Add the Account Required by the Framework</a>.
+ </dd>
+ <dt>Settings attributes</dt>
+ <dd>
+ <dl>
+ <dt>
+ {@code android:userVisible}
+ </dt>
+ <dd>
+ Sets the visibility of the sync adapter's account type. By default, the
+ account icon and label associated with the account type are visible in the
+ <b>Accounts</b> section of the system's Settings app, so you should make your sync
+ adapter invisible unless you have an account type or domain that's easily associated
+ with your app. If you make your account type invisible, you can still allow users to
+ control your sync adapter with a user interface in one of your app's activities.
+ </dd>
+ <dt>
+ {@code android:supportsUploading}
+ </dt>
+ <dd>
+ Allows you to upload data to the cloud. Set this to {@code false} if your app only
+ downloads data.
+ </dd>
+ <dt>
+ {@code android:allowParallelSyncs}
+ </dt>
+ <dd>
+ Allows multiple instances of your sync adapter component to run at the same time.
+ Use this if your app supports multiple user accounts and you want to allow multiple
+ users to transfer data in parallel. This flag has no effect if you never run
+ multiple data transfers.
+ </dd>
+ <dt>
+ {@code android:isAlwaysSyncable}
+ </dt>
+ <dd>
+ Indicates to the sync adapter framework that it can run your sync adapter at any
+ time you've specified. If you want to programmatically control when your sync
+ adapter can run, set this flag to {@code false}, and then call
+ {@link android.content.ContentResolver#requestSync requestSync()} to run the
+ sync adapter. To learn more about running a sync adapter, see the lesson
+ <a href="running-sync-adapter.html">Running a Sync Adapter</a>
+ </dd>
+ </dl>
+ </dd>
+</dl>
+<p>
+ The following example shows the XML for a sync adapter that uses a single dummy account and
+ only does downloads.
+</p>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<sync-adapter
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:contentAuthority="com.example.android.datasync.provider"
+ android:accountType="com.android.example.datasync"
+ android:userVisible="false"
+ android:supportsUploading="false"
+ android:allowParallelSyncs="false"
+ android:isAlwaysSyncable="true"/>
+</pre>
+
+<h2 id="DeclareSyncAdapterManifest">Declare the Sync Adapter in the Manifest</h2>
+<p>
+ Once you've added the sync adapter component to your app, you have to request permissions
+ related to using the component, and you have to declare the bound {@link android.app.Service}
+ you've added.
+</p>
+<p>
+ Since the sync adapter component runs code that transfers data between the network and the
+ device, you need to request permission to access the Internet. In addition, your app needs
+ to request permission to read and write sync adapter settings, so you can control the sync
+ adapter programmatically from other components in your app. You also need to request a
+ special permission that allows your app to use the authenticator component you created
+ in the lesson <a href="creating-authenticator.html">Creating a Stub Authenticator</a>.
+</p>
+<p>
+ To request these permissions, add the following to your app manifest as child elements of
+<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code>:
+</p>
+<dl>
+ <dt>
+ {@link android.Manifest.permission#INTERNET android.permission.INTERNET}
+ </dt>
+ <dd>
+ Allows the sync adapter code to access the Internet so that it can download or upload data
+ from the device to a server. You don't need to add this permission again if you were
+ requesting it previously.
+ </dd>
+ <dt>
+{@link android.Manifest.permission#READ_SYNC_SETTINGS android.permission.READ_SYNC_SETTINGS}
+ </dt>
+ <dd>
+ Allows your app to read the current sync adapter settings. For example, you need this
+ permission in order to call {@link android.content.ContentResolver#getIsSyncable
+ getIsSyncable()}.
+ </dd>
+ <dt>
+{@link android.Manifest.permission#WRITE_SYNC_SETTINGS android.permission.WRITE_SYNC_SETTINGS}
+ </dt>
+ <dd>
+ Allows your app to control sync adapter settings. You need this permission in order to
+ set periodic sync adapter runs using {@link android.content.ContentResolver#addPeriodicSync
+ addPeriodicSync()}. This permission is <b>not</b> required to call
+ {@link android.content.ContentResolver#requestSync requestSync()}. To learn more about
+ running the sync adapter, see <a href="running-sync-adapter.html"
+ >Running A Sync Adapter</a>.
+ </dd>
+ <dt>
+{@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS android.permission.AUTHENTICATE_ACCOUNTS}
+ </dt>
+ <dd>
+ Allows you to use the authenticator component you created in the lesson
+ <a href="creating-authenticator.html">Creating a Stub Authenticator</a>.
+ </dd>
+</dl>
+<p>
+ The following snippet shows how to add the permissions:
+</p>
+<pre>
+<manifest>
+...
+ <uses-permission
+ android:name="android.permission.INTERNET"/>
+ <uses-permission
+ android:name="android.permission.READ_SYNC_SETTINGS"/>
+ <uses-permission
+ android:name="android.permission.WRITE_SYNC_SETTINGS"/>
+ <uses-permission
+ android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
+...
+</manifest>
+</pre>
+<p>
+ Finally, to declare the bound {@link android.app.Service} that the framework uses to
+ interact with your sync adapter, add the following XML to your app manifest as a child element
+ of <code><a href="{@docRoot}guide/topics/manifest/application-element.html"
+ ><application></a></code>:
+</p>
+<pre>
+ <service
+ android:name="com.example.android.datasync.SyncService"
+ android:exported="true"
+ android:process=":sync">
+ <intent-filter>com.example.android.datasync.provider
+ <action android:name="android.content.SyncAdapter"/>
+ </intent-filter>
+ <meta-data android:name="android.content.SyncAdapter"
+ android:resource="@xml/syncadapter" />
+ </service>
+</pre>
+<p>
+ The
+<code><a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"><intent-filter></a></code>
+ element sets up a filter that's triggered by the intent action
+ {@code android.content.SyncAdapter}, sent by the system to run the sync adapter. When the filter
+ is triggered, the system starts the bound service you've created, which in this example is
+ {@code SyncService}. The attribute
+<code><a href="{@docRoot}guide/topics/manifest/service-element.html#exported">android:exported="true"</a></code>
+ allows processes other than your app (including the system) to access the
+ {@link android.app.Service}. The attribute
+<code><a href="{@docRoot}guide/topics/manifest/service-element.html#proc">android:process=":sync"</a></code>
+ tells the system to run the {@link android.app.Service} in a global shared process named
+ {@code sync}. If you have multiple sync adapters in your app they can share this process,
+ which reduces overhead.
+</p>
+<p>
+ The
+<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data></a></code>
+ element provides provides the name of the sync adapter metadata XML file you created previously.
+ The
+<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#nm">android:name</a></code>
+ attribute indicates that this metadata is for the sync adapter framework. The
+<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#rsrc">android:resource</a></code>
+ element specifies the name of the metadata file.
+</p>
+<p>
+ You now have all of the components for your sync adapter. The next lesson shows you how to
+ tell the sync adapter framework to run your sync adapter, either in response to an event or on
+ a regular schedule.
+</p>
--- /dev/null
+page.title=Transferring Data Using Sync Adapters
+
+trainingnavtop=true
+startpage=true
+
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+<ul>
+ <li>Android 3.0 (API Level 11) or higher</li>
+</ul>
+
+<h2>You should also read</h2>
+<ul>
+ <li>
+ <a href="{@docRoot}guide/components/bound-services.html">Bound Services</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>
+ </li>
+ <li>
+ <a href="{@docRoot}training/id-auth/custom_auth.html">Creating a Custom Account Type</a>
+ </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="http://developer.android.com/shareables/training/BasicSyncAdapter.zip" class="button">Download the sample</a>
+ <p class="filename">BasicSyncAdapter.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+ Synchronizing data between an Android device and web servers can make your application
+ significantly more useful and compelling for your users. For example, transferring data to a web
+ server makes a useful backup, and transferring data from a server makes it available to the user
+ even when the device is offline. In some cases, users may find it easier to enter and edit their
+ data in a web interface and then have that data available on their device, or they may want to
+ collect data over time and then upload it to a central storage area.
+</p>
+<p>
+ Although you can design your own system for doing data transfers in your app, you should
+ consider using Android's sync adapter framework. This framework helps manage and automate data
+ transfers, and coordinates synchronization operations across different apps. When you use
+ this framework, you can take advantage of several features that aren't available to data
+ transfer schemes you design yourself:
+</p>
+<dl>
+ <dt>
+ Plug-in architecture
+ </dt>
+ <dd>
+ Allows you to add data transfer code to the system in the form of callable components.
+ </dd>
+ <dt>
+ Automated execution
+ </dt>
+ <dd>
+ Allows you to automate data transfer based on a variety of criteria, including data changes,
+ elapsed time, or time of day. In addition, the system adds transfers that are unable to
+ run to a queue, and runs them when possible.
+ </dd>
+ <dt>
+ Automated network checking
+ </dt>
+ <dd>
+ The system only runs your data transfer when the device has network connectivity.
+ </dd>
+ <dt>
+ Improved battery performance
+ </dt>
+ <dd>
+ Allows you to centralize all of your app's data transfer tasks in one place, so that they
+ all run at the same time. Your data transfer is also scheduled in conjunction with data
+ transfers from other apps. These factors reduce the number of times the system has to
+ switch on the network, which reduces battery usage.
+ </dd>
+ <dt>
+ Account management and authentication
+ </dt>
+ <dd>
+ If your app requires user credentials or server login, you can optionally
+ integrate account management and authentication into your data transfer.
+ </dd>
+</dl>
+<p>
+ This class shows you how to create a sync adapter and the bound {@link android.app.Service} that
+ wraps it, how to provide the other components that help you plug the sync adapter into the
+ framework, and how to run the sync adapter to run in various ways.
+</p>
+<p class="note">
+ <strong>Note:</strong> Sync adapters run asynchronously, so you should use them with the
+ expectation that they transfer data regularly and efficiently, but not instantaneously. If
+ you need to do real-time data transfer, you should do it in an {@link android.os.AsyncTask} or
+ an {@link android.app.IntentService}.
+</p>
+<h2>Lessons</h2>
+<dl>
+ <dt>
+ <b><a href="creating-authenticator.html">Creating a Stub Authenticator</a></b>
+ </dt>
+ <dd>
+ Learn how to add an account-handling component that the sync adapter framework expects to be
+ part of your app. This lesson shows you how to create a stub authentication component for
+ simplicity.
+ </dd>
+ <dt>
+ <b><a href="creating-stub-provider.html">Creating a Stub Content Provider</a></b>
+ </dt>
+ <dd>
+ Learn how to add a content provider component that the sync adapter framework expects to be
+ part of your app. This lesson assumes that your app doesn't use a content provider, so it
+ shows you how to add a stub component. If you have a content provider already in your app,
+ you can skip this lesson.
+ </dd>
+ <dt>
+ <b><a href="creating-sync-adapter.html">Creating a Sync Adapter</a></b>
+ </dt>
+ <dd>
+ Learn how to encapsulate your data transfer code in a component that the sync
+ adapter framework can run automatically.
+ </dd>
+ <dt>
+ <b><a href="running-sync-adapter.html">Running a Sync Adapter</a></b>
+ </dt>
+ <dd>
+ Learn how to trigger and schedule data transfers using the sync adapter framework.
+ </dd>
+</dl>
--- /dev/null
+page.title=Running a Sync Adapter
+
+trainingnavtop=true
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you how to:</h2>
+<ol>
+ <li><a href="#RunByMessage">Run the Sync Adapter When Server Data Changes</a>
+ <li><a href="#RunDataChange">Run the Sync Adapter When Content Provider Data Changes</a></li>
+ <li><a href="#RunByNetwork">Run the Sync Adapter After a Network Message</a></li>
+ <li><a href="#RunPeriodic">Run the Sync Adapter Periodically</a></li>
+ <li><a href="#RunOnDemand">Run the Sync Adapter On Demand</a></li>
+</ol>
+
+
+<h2>You should also read</h2>
+<ul>
+ <li>
+ <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>
+ </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="http://developer.android.com/shareables/training/BasicSyncAdapter.zip" class="button">Download the sample</a>
+ <p class="filename">BasicSyncAdapter.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+ In the previous lessons in this class, you learned how to create a sync adapter component that
+ encapsulates data transfer code, and how to add the additional components that allow you to
+ plug the sync adapter into the system. You now have everything you need to install an app that
+ includes a sync adapter, but none of the code you've seen actually runs the sync adapter.
+</p>
+<p>
+ You should try to run your sync adapter based on a schedule or as the indirect result of some
+ event. For example, you may want your sync adapter to run on a regular schedule, either after a
+ certain period of time or at a particular time of the day. You may also want to run your sync
+ adapter when there are changes to data stored on the device. You should avoid running your
+ sync adapter as the direct result of a user action, because by doing this you don't get the full
+ benefit of the sync adapter framework's scheduling ability. For example, you should avoid
+ providing a refresh button in your user interface.
+</p>
+<p>
+ You have the following options for running your sync adapter:
+</p>
+<dl>
+ <dt>
+ When server data changes
+ </dt>
+ <dd>
+ Run the sync adapter in response to a message from a server, indicating that server-based
+ data has changed. This option allows you to refresh data from the server to the device
+ without degrading performance or wasting battery life by polling the server.
+ </dd>
+ <dt>When device data changes</dt>
+ <dd>
+ Run a sync adapter when data changes on the device. This option allows you to send
+ modified data from the device to a server, and is especially useful if you need to ensure
+ that the server always has the latest device data. This option is straightforward to
+ implement if you actually store data in your content provider. If you're using a stub
+ content provider, detecting data changes may be more difficult.
+ </dd>
+ <dt>
+ When the system sends out a network message
+ </dt>
+ <dd>
+ Run a sync adapter when the Android system sends out a network message that keeps the
+ TCP/IP connection open; this message is a basic part of the networking framework. Using
+ this option is one way to run the sync adapter automatically. Consider using it in
+ conjunction with interval-based sync adapter runs.
+ </dd>
+ <dt>
+ At regular intervals
+ </dt>
+ <dd>
+ Run a sync adapter after the expiration of an interval you choose, or run it at a certain
+ time every day.
+ </dd>
+ <dt>On demand</dt>
+ <dd>
+ Run the sync adapter in response to a user action. However, to provide the best user
+ experience you should rely primarily on one of the more automated options. By using
+ automated options, you conserve battery and network resources.
+ </dd>
+</dl>
+<p>
+ The rest of this lesson describes each of the options in more detail.
+</p>
+<h2 id="RunByMessage">Run the Sync Adapter When Server Data Changes</h2>
+<p>
+ If your app transfers data from a server and the server data changes frequently, you can use
+ a sync adapter to do downloads in response to data changes. To run the sync adapter, have
+ the server send a special message to a {@link android.content.BroadcastReceiver} in your app.
+ In response to this message, call {@link android.content.ContentResolver#requestSync
+ ContentResolver.requestSync()} to signal the sync adapter framework to run your
+ sync adapter.
+</p>
+<p>
+ <a href="{@docRoot}google/gcm/index.html">Google Cloud Messaging</a> (GCM) provides both the
+ server and device components you need to make this messaging system work. Using GCM to trigger
+ transfers is more reliable and more efficient than polling servers for status. While polling
+ requires a {@link android.app.Service} that is always active, GCM uses a
+ {@link android.content.BroadcastReceiver} that's activated when a message arrives. While polling
+ at regular intervals uses battery power even if no updates are available, GCM only sends
+ messages when needed.
+</p>
+<p class="note">
+ <strong>Note:</strong> If you use GCM to trigger your sync adapter via a broadcast to all
+ devices where your app is installed, remember that they receive your message at
+ roughly the same time. This situation can cause multiple instance of your sync adapter to run
+ at the same time, causing server and network overload. To avoid this situation for a broadcast
+ to all devices, you should consider deferring the start of the sync adapter for a period
+ that's unique for each device.
+<p>
+ The following code snippet shows you how to run
+ {@link android.content.ContentResolver#requestSync requestSync()} in response to an
+ incoming GCM message:
+</p>
+<pre>
+public class GcmBroadcastReceiver extends BroadcastReceiver {
+ ...
+ // Constants
+ // Content provider authority
+ public static final String AUTHORITY = "com.example.android.datasync.provider"
+ // Account type
+ public static final String ACCOUNT_TYPE = "com.example.android.datasync";
+ // Account
+ public static final String ACCOUNT = "default_account";
+ // Incoming Intent key for extended data
+ public static final String KEY_SYNC_REQUEST =
+ "com.example.android.datasync.KEY_SYNC_REQUEST";
+ ...
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Get a GCM object instance
+ GoogleCloudMessaging gcm =
+ GoogleCloudMessaging.getInstance(context);
+ // Get the type of GCM message
+ String messageType = gcm.getMessageType(intent);
+ /*
+ * Test the message type and examine the message contents.
+ * Since GCM is a general-purpose messaging system, you
+ * may receive normal messages that don't require a sync
+ * adapter run.
+ * The following code tests for a a boolean flag indicating
+ * that the message is requesting a transfer from the device.
+ */
+ if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)
+ &&
+ intent.getBooleanExtra(KEY_SYNC_REQUEST)) {
+ /*
+ * Signal the framework to run your sync adapter. Assume that
+ * app initialization has already created the account.
+ */
+ ContentResolver.requestSync(ACCOUNT, AUTHORITY, null);
+ ...
+ }
+ ...
+ }
+ ...
+}
+</pre>
+<h2 id="RunDataChange">Run the Sync Adapter When Content Provider Data Changes</h2>
+<p>
+ If your app collects data in a content provider, and you want to update the server whenever
+ you update the provider, you can set up your app to run your sync adapter automatically. To do
+ this, you register an observer for the content provider. When data in your content provider
+ changes, the content provider framework calls the observer. In the observer, call
+ {@link android.content.ContentResolver#requestSync requestSync()} to tell the framework to run
+ your sync adapter.
+</p>
+<p class="note">
+ <strong>Note:</strong> If you're using a stub content provider, you don't have any data in
+ the content provider and {@link android.database.ContentObserver#onChange onChange()} is
+ never called. In this case, you have to provide your own mechanism for detecting changes to
+ device data. This mechanism is also responsible for calling
+ {@link android.content.ContentResolver#requestSync requestSync()} when the data changes.
+</p>
+<p>
+ To create an observer for your content provider, extend the class
+ {@link android.database.ContentObserver} and implement both forms of its
+ {@link android.database.ContentObserver#onChange onChange()} method. In
+ {@link android.database.ContentObserver#onChange onChange()}, call
+ {@link android.content.ContentResolver#requestSync requestSync()} to start the sync adapter.
+</p>
+<p>
+ To register the observer, pass it as an argument in a call to
+ {@link android.content.ContentResolver#registerContentObserver registerContentObserver()}. In
+ this call, you also have to pass in a content URI for the data you want to watch. The content
+ provider framework compares this watch URI to content URIs passed in as arguments to
+ {@link android.content.ContentResolver} methods that modify your provider, such as
+ {@link android.content.ContentResolver#insert ContentResolver.insert()}. If there's a match, your
+ implementation of {@link android.database.ContentObserver#onChange ContentObserver.onChange()}
+ is called.
+</p>
+
+<p>
+ The following code snippet shows you how to define a {@link android.database.ContentObserver}
+ that calls {@link android.content.ContentResolver#requestSync requestSync()} when a table
+ changes:
+</p>
+<pre>
+public class MainActivity extends FragmentActivity {
+ ...
+ // Constants
+ // Content provider scheme
+ public static final String SCHEME = "content://";
+ // Content provider authority
+ public static final String AUTHORITY = "com.example.android.datasync.provider";
+ // Path for the content provider table
+ public static final String TABLE_PATH = "data_table";
+ // Account
+ public static final String ACCOUNT = "default_account";
+ // Global variables
+ // A content URI for the content provider's data table
+ Uri mUri;
+ // A content resolver for accessing the provider
+ ContentResolver mResolver;
+ ...
+ public class TableObserver extends ContentObserver {
+ /*
+ * Define a method that's called when data in the
+ * observed content provider changes.
+ * This method signature is provided for compatibility with
+ * older platforms.
+ */
+ @Override
+ public void onChange(boolean selfChange) {
+ /*
+ * Invoke the method signature available as of
+ * Android platform version 4.1, with a null URI.
+ */
+ onChange(selfChange, null);
+ }
+ /*
+ * Define a method that's called when data in the
+ * observed content provider changes.
+ */
+ @Override
+ public void onChange(boolean selfChange, Uri changeUri) {
+ /*
+ * Ask the framework to run your sync adapter.
+ * To maintain backward compatibility, assume that
+ * changeUri is null.
+ ContentResolver.requestSync(ACCOUNT, AUTHORITY, null);
+ }
+ ...
+ }
+ ...
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ...
+ // Get the content resolver object for your app
+ mResolver = getContentResolver();
+ // Construct a URI that points to the content provider data table
+ mUri = new Uri.Builder()
+ .scheme(SCHEME)
+ .authority(AUTHORITY)
+ .path(TABLE_PATH)
+ .build();
+ /*
+ * Create a content observer object.
+ * Its code does not mutate the provider, so set
+ * selfChange to "false"
+ */
+ TableObserver observer = new TableObserver(false);
+ /*
+ * Register the observer for the data table. The table's path
+ * and any of its subpaths trigger the observer.
+ */
+ mResolver.registerContentObserver(mUri, true, observer);
+ ...
+ }
+ ...
+}
+</pre>
+<h2 id="RunByNetwork">Run the Sync Adapter After a Network Message</h2>
+<p>
+ When a network connection is available, the Android system sends out a message
+ every few seconds to keep the device's TCP/IP connection open. This message also goes to
+ the {@link android.content.ContentResolver} of each app. By calling
+ {@link android.content.ContentResolver#setSyncAutomatically setSyncAutomatically()},
+ you can run the sync adapter whenever the {@link android.content.ContentResolver}
+ receives the message.
+</p>
+<p>
+ By scheduling your sync adapter to run when the network message is sent, you ensure that your
+ sync adapter is always scheduled to run while the network is available. Use this option if you
+ don't have to force a data transfer in response to data changes, but you do want to ensure
+ your data is regularly updated. Similarly, you can use this option if you don't want a fixed
+ schedule for your sync adapter, but you do want it to run frequently.
+</p>
+<p>
+ Since the method
+ {@link android.content.ContentResolver#setSyncAutomatically setSyncAutomatically()}
+ doesn't disable {@link android.content.ContentResolver#addPeriodicSync addPeriodicSync()}, your
+ sync adapter may be triggered repeatedly in a short period of time. If you do want to run
+ your sync adapter periodically on a regular schedule, you should disable
+ {@link android.content.ContentResolver#setSyncAutomatically setSyncAutomatically()}.
+</p>
+<p>
+ The following code snippet shows you how to configure your
+ {@link android.content.ContentResolver} to run your sync adapter in response to a network
+ message:
+</p>
+<pre>
+public class MainActivity extends FragmentActivity {
+ ...
+ // Constants
+ // Content provider authority
+ public static final String AUTHORITY = "com.example.android.datasync.provider";
+ // Account
+ public static final String ACCOUNT = "default_account";
+ // Global variables
+ // A content resolver for accessing the provider
+ ContentResolver mResolver;
+ ...
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ...
+ // Get the content resolver for your app
+ mResolver = getContentResolver();
+ // Turn on automatic syncing for the default account and authority
+ mResolver.setSyncAutomatically(ACCOUNT, AUTHORITY, true);
+ ...
+ }
+ ...
+}
+</pre>
+<h2 id="RunPeriodic">Run the Sync Adapter Periodically</h2>
+<p>
+ You can run your sync adapter periodically by setting a period of time to wait between runs,
+ or by running it at certain times of the day, or both. Running your sync adapter
+ periodically allows you to roughly match the update interval of your server.
+</p>
+<p>
+ Similarly, you can upload data from the device when your server is relatively idle, by
+ scheduling your sync adapter to run at night. Most users leave their powered on and plugged in
+ at night, so this time is usually available. Moreover, the device is not running other tasks at
+ the same time as your sync adapter. If you take this approach, however, you need to ensure that
+ each device triggers a data transfer at a slightly different time. If all devices run your
+ sync adapter at the same time, you are likely to overload your server and cell provider data
+ networks.
+</p>
+<p>
+ In general, periodic runs make sense if your users don't need instant updates, but expect to
+ have regular updates. Periodic runs also make sense if you want to balance the availability of
+ up-to-date data with the efficiency of smaller sync adapter runs that don't over-use device
+ resources.
+</p>
+<p>
+ To run your sync adapter at regular intervals, call
+ {@link android.content.ContentResolver#addPeriodicSync addPeriodicSync()}. This schedules your
+ sync adapter to run after a certain amount of time has elapsed. Since the sync adapter framework
+ has to account for other sync adapter executions and tries to maximize battery efficiency, the
+ elapsed time may vary by a few seconds. Also, the framework won't run your sync adapter if the
+ network is not available.
+</p>
+<p>
+ Notice that {@link android.content.ContentResolver#addPeriodicSync addPeriodicSync()} doesn't
+ run the sync adapter at a particular time of day. To run your sync adapter at roughly the
+ same time every day, use a repeating alarm as a trigger. Repeating alarms are described in more
+ detail in the reference documentation for {@link android.app.AlarmManager}. If you use the
+ method {@link android.app.AlarmManager#setInexactRepeating setInexactRepeating()} to set
+ time-of-day triggers that have some variation, you should still randomize the start time to
+ ensure that sync adapter runs from different devices are staggered.
+</p>
+<p>
+ The method {@link android.content.ContentResolver#addPeriodicSync addPeriodicSync()} doesn't
+ disable {@link android.content.ContentResolver#setSyncAutomatically setSyncAutomatically()},
+ so you may get multiple sync runs in a relatively short period of time. Also, only a few
+ sync adapter control flags are allowed in a call to
+ {@link android.content.ContentResolver#addPeriodicSync addPeriodicSync()}; the flags that are
+ not allowed are described in the referenced documentation for
+ {@link android.content.ContentResolver#addPeriodicSync addPeriodicSync()}.
+</p>
+<p>
+ The following code snippet shows you how to schedule periodic sync adapter runs:
+</p>
+<pre>
+public class MainActivity extends FragmentActivity {
+ ...
+ // Constants
+ // Content provider authority
+ public static final String AUTHORITY = "com.example.android.datasync.provider";
+ // Account
+ public static final String ACCOUNT = "default_account";
+ // Sync interval constants
+ public static final long MILLISECONDS_PER_SECOND = 1000L;
+ public static final long SECONDS_PER_MINUTE = 60L;
+ public static final long SYNC_INTERVAL_IN_MINUTES = 60L;
+ public static final long SYNC_INTERVAL =
+ SYNC_INTERVAL_IN_MINUTES *
+ SECONDS_PER_MINUTE *
+ MILLISECONDS_PER_SECOND;
+ // Global variables
+ // A content resolver for accessing the provider
+ ContentResolver mResolver;
+ ...
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ...
+ // Get the content resolver for your app
+ mResolver = getContentResolver();
+ /*
+ * Turn on periodic syncing
+ */
+ ContentResolver.addPeriodicSync(
+ ACCOUNT,
+ AUTHORITY,
+ null,
+ SYNC_INTERVAL);
+ ...
+ }
+ ...
+}
+</pre>
+<h2 id="RunOnDemand">Run the Sync Adapter On Demand</h2>
+<p>
+ Running your sync adapter in response to a user request is the least preferable strategy
+ for running a sync adapter. The framework is specifically designed to conserve battery power
+ when it runs sync adapters according to a schedule. Options that run a sync in response to data
+ changes use battery power effectively, since the power is used to provide new data.
+</p>
+<p>
+ In comparison, allowing users to run a sync on demand means that the sync runs by itself, which
+ is inefficient use of network and power resources. Also, providing sync on demand leads users to
+ request a sync even if there's no evidence that the data has changed, and running a sync that
+ doesn't refresh data is an ineffective use of battery power. In general, your app should either
+ use other signals to trigger a sync or schedule them at regular intervals, without user input.
+</p>
+<p>
+ However, if you still want to run the sync adapter on demand, set the sync adapter flags for a
+ manual sync adapter run, then call
+ {@link android.content.ContentResolver#requestSync ContentResolver.requestSync()}.
+</p>
+<p>
+ Run on demand transfers with the following flags:
+</p>
+<dl>
+ <dt>
+ {@link android.content.ContentResolver#SYNC_EXTRAS_MANUAL SYNC_EXTRAS_MANUAL}
+ </dt>
+ <dd>
+ Forces a manual sync. The sync adapter framework ignores the existing settings,
+ such as the flag set by {@link android.content.ContentResolver#setSyncAutomatically
+ setSyncAutomatically()}.
+ </dd>
+ <dt>
+ {@link android.content.ContentResolver#SYNC_EXTRAS_EXPEDITED SYNC_EXTRAS_EXPEDITED}
+ </dt>
+ <dd>
+ Forces the sync to start immediately. If you don't set this, the system may wait several
+ seconds before running the sync request, because it tries to optimize battery use by
+ scheduling many requests in a short period of time.
+ </dd>
+</dl>
+<p>
+ The following code snippet shows you how to call
+ {@link android.content.ContentResolver#requestSync requestSync()} in response to a button
+ click:
+</p>
+<pre>
+public class MainActivity extends FragmentActivity {
+ ...
+ // Constants
+ // Content provider authority
+ public static final String AUTHORITY =
+ "com.example.android.datasync.provider"
+ // Account type
+ public static final String ACCOUNT_TYPE = "com.example.android.datasync";
+ // Account
+ public static final String ACCOUNT = "default_account";
+ // Instance fields
+ Account mAccount;
+ ...
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ...
+ /*
+ * Create the dummy account. The code for CreateSyncAccount
+ * is listed in the lesson Creating a Sync Adapter
+ */
+
+ mAccount = CreateSyncAccount(this);
+ ...
+ }
+ /**
+ * Respond to a button click by calling requestSync(). This is an
+ * asynchronous operation.
+ *
+ * This method is attached to the refresh button in the layout
+ * XML file
+ *
+ * @param v The View associated with the method call,
+ * in this case a Button
+ */
+ public void onRefreshButtonClick(View v) {
+ ...
+ // Pass the settings flags by inserting them in a bundle
+ Bundle settingsBundle = new Bundle();
+ settingsBundle.putBoolean(
+ ContentResolver.SYNC_EXTRAS_MANUAL, true);
+ settingsBundle.putBoolean(
+ ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+ /*
+ * Request the sync for the default account, authority, and
+ * manual sync settings
+ */
+ ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);
+ }
+</pre>
</a>
</li>
</li>
+ <li class="nav-section">
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot ?>training/sync-adapters/index.html"
+ description="How to transfer data between the cloud and the device using the Android
+ sync adapter framework"
+ >Transferring Data Using Sync Adapters</a>
+ </div>
+ <ul>
+ <li>
+ <a href="<?cs var:toroot ?>training/sync-adapters/creating-authenticator.html">
+ Creating a Stub Authenticator
+ </a>
+ </li>
+ <li>
+ <a href="<?cs var:toroot ?>training/sync-adapters/creating-stub-provider.html">
+ Creating a Stub Content Provider
+ </a>
+ </li>
+ <li>
+ <a href="<?cs var:toroot ?>training/sync-adapters/creating-sync-adapter.html">
+ Creating a Sync Adapter
+ </a>
+ </li>
+ <li>
+ <a href="<?cs var:toroot ?>training/sync-adapters/running-sync-adapter.html">
+ Running a Sync Adapter
+ </a>
+ </li>
+ </ul>
+ </li>
+
</ul>
</li>
<!-- End connectivity and cloud -->