diff --git a/docs/downloads/training/BasicSyncAdapter.zip b/docs/downloads/training/BasicSyncAdapter.zip new file mode 100644 index 0000000000000..25aa8bb53d594 Binary files /dev/null and b/docs/downloads/training/BasicSyncAdapter.zip differ diff --git a/docs/html/training/sync-adapters/creating-authenticator.jd b/docs/html/training/sync-adapters/creating-authenticator.jd new file mode 100644 index 0000000000000..1b272e7b59940 --- /dev/null +++ b/docs/html/training/sync-adapters/creating-authenticator.jd @@ -0,0 +1,290 @@ +page.title=Creating a Stub Authenticator + +trainingnavtop=true +@jd:body + + +
+
+ +

This lesson teaches you to

+
    +
  1. + Add a Stub Authenticator Component +
  2. +
  3. + Bind the Authenticator to the Framework +
  4. +
  5. + Add the Authenticator Metadata File +
  6. +
  7. + Declare the Authenticator in the Manifest +
  8. +
+ +

You should also read

+ + +

Try it out

+ +
+ Download the sample +

BasicSyncAdapter.zip

+
+ +
+
+

+ 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. +

+

+ 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. +

+

+ 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}. +

+ +

Add a Stub Authenticator Component

+

+ 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. +

+

+ The following snippet shows an example of a stub authenticator class: +

+
+/*
+ * 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();
+    }
+}
+
+

Bind the Authenticator to the Framework

+

+ 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. +

+

+ 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. +

+

+ The following snippet shows you how to define the bound {@link android.app.Service}: +

+
+/**
+ * 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();
+    }
+}
+
+ +

Add the Authenticator Metadata File

+

+ 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}. +

+

+ This XML file contains a single element <account-authenticator> that + has the following attributes: +

+
+
+ android:accountType +
+
+ 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. +

+ 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. +

+
+
+ android:icon +
+
+ Pointer to a Drawable + resource containing an icon. If you make the sync adapter visible by specifying the + attribute android:userVisible="true" in res/xml/syncadapter.xml, + then you must provide this icon resource. It appears in the Accounts section of + the system's Settings app. +
+
+ android:smallIcon +
+
+ Pointer to a Drawable + resource containing a small version of the icon. This resource may be used instead of + android:icon in the Accounts section of the system's Settings app, + depending on the screen size. +
+
+ android:label +
+
+ Localizable string that identifies the account type to users. If you make the sync adapter + visible by specifying the attribute android:userVisible="true" in + res/xml/syncadapter.xml, then you should provide this string. It appears in the + Accounts section of the system's Settings app, next to the icon you define for the + authenticator. +
+
+

+ The following snippet shows the XML file for the authenticator you created previously: +

+
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"/>
+
+ +

Declare the Authenticator in the Manifest

+

+ 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 + <service> + element as a child element of +<application>: +

+
+    <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>
+
+

+ The +<intent-filter> + 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. +

+

+ The +<meta-data> + element declares the metadata for the authenticator. The +android:name + attribute links the meta-data to the authentication framework. The +android:resource + element specifies the name of the authenticator metadata file you created previously. +

+

+ 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 Creating a Sync Adapter. +

diff --git a/docs/html/training/sync-adapters/creating-stub-provider.jd b/docs/html/training/sync-adapters/creating-stub-provider.jd new file mode 100644 index 0000000000000..8f6eba037ef7b --- /dev/null +++ b/docs/html/training/sync-adapters/creating-stub-provider.jd @@ -0,0 +1,203 @@ +page.title=Creating a Stub Content Provider + +trainingnavtop=true +@jd:body + + +
+
+ +

This lesson teaches you to

+
    +
  1. + Add a Stub Content Provider +
  2. +
  3. + Declare the Provider in the Manifest +
  4. +
+ +

You should also read

+ + +

Try it out

+ +
+ Download the sample +

BasicSyncAdapter.zip

+
+ +
+
+

+ 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. +

+

+ 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 Creating a Content Provider. +

+

+ 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. +

+

+ 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 + Creating a Sync Adapter. 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. +

+

Add a Stub Content Provider

+

+ 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: +

+
+/*
+ * 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;
+    }
+}
+
+

Declare the Provider in the Manifest

+

+ 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 <provider> element with the following attributes: +

+
+
+ android:name="com.example.android.datasync.provider.StubProvider" +
+
+ Specifies the fully-qualified name of the class that implements the stub content provider. +
+
+ android:authorities="com.example.android.datasync.provider" +
+
+ 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. +
+
+ android:exported="false" +
+
+ 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. +
+
+ android:syncable="true" +
+
+ 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. +
+
+

+ The following snippet shows you how to add the + <provider> element to the app manifest: +

+
+<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>
+
+

+ 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. +

diff --git a/docs/html/training/sync-adapters/creating-sync-adapter.jd b/docs/html/training/sync-adapters/creating-sync-adapter.jd new file mode 100644 index 0000000000000..7c59c8c69703c --- /dev/null +++ b/docs/html/training/sync-adapters/creating-sync-adapter.jd @@ -0,0 +1,658 @@ +page.title=Creating a Sync Adapter + +trainingnavtop=true +@jd:body + +
+
+ +

This lesson teaches you to

+
    +
  1. + Create the Sync Adapter Class +
  2. +
  3. + Bind the Sync Adapter to the Framework +
  4. +
  5. + Add the Account Required by the Framework +
  6. +
  7. + Add the Sync Adapter Metadata File +
  8. +
  9. + Declare the Sync Adapter in the Manifest +
  10. +
+ +

You should also read

+ + +

Try it out

+ +
+ Download the sample +

BasicSyncAdapter.zip

+
+ +
+
+

+ 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: +

+
+ Sync adapter class. +
+
+ A class that wraps your data transfer code in an interface compatible with the sync adapter + framework. +
+
+ Bound {@link android.app.Service}. +
+
+ A component that allows the sync adapter framework to run the code in your sync adapter + class. +
+
+ Sync adapter XML metadata file. +
+
+ A file containing information about your sync adapter. The framework reads this file to + find out how to load and schedule your data transfer. +
+
+ Declarations in the app manifest. +
+
+ XML that declares the bound service and points to sync adapter-specific metadata. +
+
+

+ This lesson shows you how to define these elements. +

+

Create a Sync Adapter Class

+

+ 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. +

+

Extend the base sync adapter class AbstractThreadedSyncAdapter

+

+ 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. +

+

+ Note: 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 + Bind the Sync Adapter to the Framework. +

+

+ The following example shows you how to implement + {@link android.content.AbstractThreadedSyncAdapter}and its constructors: +

+
+/**
+ * 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();
+        ...
+    }
+
+

Add the data transfer code to onPerformSync()

+

+ 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()}. +

+

+ 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: +

+
+
+ Account +
+
+ 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. +
+
+ Extras +
+
+ A {@link android.os.Bundle} containing flags sent by the event that triggered the sync + adapter. +
+
+ Authority +
+
+ 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. +
+
+ Content provider client +
+
+ 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. +
+
+ Sync result +
+
+ A {@link android.content.SyncResult} object that you use to send information to the sync + adapter framework. +
+
+

+ The following snippet shows the overall structure of + {@link android.content.AbstractThreadedSyncAdapter#onPerformSync onPerformSync()}: +

+
+    /*
+     * 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.
+     */
+    ...
+    }
+
+

+ 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: +

+
+
+ Connecting to a server +
+
+ 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. +
+
+ Downloading and uploading data +
+
+ 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. +
+
+ Handling data conflicts or determining how current the data is +
+
+ 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. +
+
+ Clean up. +
+
+ Always close connections to a server and clean up temp files and caches at the end of + your data transfer. +
+
+

+ Note: 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. +

+

+ 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 Transferring Data Without Draining the Battery, which describes several network access + tasks you can include in your data transfer code. +

+

Bind the Sync Adapter to the Framework

+

+ 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. +

+

+ 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. +

+

+ 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: +

+
+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();
+    }
+}
+
+

+ Note: To see a more detailed example of a bound service for a sync adapter, + see the sample app. +

+

Add the Account Required by the Framework

+

+ The sync adapter framework requires each sync adapter to have an account type. You declared + the account type value in the section + Add the Authenticator Metadata File. 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()}. +

+

+ 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: +

+
+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.
+             */
+        }
+    }
+    ...
+}
+
+

Add the Sync Adapter Metadata File

+

+ 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}. +

+

+ This XML file contains a single XML element <sync-adapter> that has the + following attributes: +

+
+
android:contentAuthority
+
+ The URI authority for your content provider. If you created a stub content provider for + your app in the previous lesson Creating a Stub Content Provider, use the value you specified for the + attribute +android:authorities + in the <provider> element you added to your app manifest. This attribute is + described in more detail in the section + Declare the Provider in the Manifest. +
+ 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 +android:authorities + attribute of the <provider> element that declares your provider in your app manifest. +
+
android:accountType
+
+ 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 Add the Authenticator Metadata File. It's also the value you specified for the + constant {@code ACCOUNT_TYPE} in the code snippet in the section + Add the Account Required by the Framework. +
+
Settings attributes
+
+
+
+ {@code android:userVisible} +
+
+ 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 + Accounts 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. +
+
+ {@code android:supportsUploading} +
+
+ Allows you to upload data to the cloud. Set this to {@code false} if your app only + downloads data. +
+
+ {@code android:allowParallelSyncs} +
+
+ 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. +
+
+ {@code android:isAlwaysSyncable} +
+
+ 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 + Running a Sync Adapter +
+
+
+
+

+ The following example shows the XML for a sync adapter that uses a single dummy account and + only does downloads. +

+
+<?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"/>
+
+ +

Declare the Sync Adapter in the Manifest

+

+ 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. +

+

+ 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 Creating a Stub Authenticator. +

+

+ To request these permissions, add the following to your app manifest as child elements of +<manifest>: +

+
+
+ {@link android.Manifest.permission#INTERNET android.permission.INTERNET} +
+
+ 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. +
+
+{@link android.Manifest.permission#READ_SYNC_SETTINGS android.permission.READ_SYNC_SETTINGS} +
+
+ 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()}. +
+
+{@link android.Manifest.permission#WRITE_SYNC_SETTINGS android.permission.WRITE_SYNC_SETTINGS} +
+
+ 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 not required to call + {@link android.content.ContentResolver#requestSync requestSync()}. To learn more about + running the sync adapter, see Running A Sync Adapter. +
+
+{@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS android.permission.AUTHENTICATE_ACCOUNTS} +
+
+ Allows you to use the authenticator component you created in the lesson + Creating a Stub Authenticator. +
+
+

+ The following snippet shows how to add the permissions: +

+
+<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>
+
+

+ 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 <application>: +

+
+        <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>
+
+

+ The +<intent-filter> + 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 +android:exported="true" + allows processes other than your app (including the system) to access the + {@link android.app.Service}. The attribute +android:process=":sync" + 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. +

+

+ The +<meta-data> + element provides provides the name of the sync adapter metadata XML file you created previously. + The +android:name + attribute indicates that this metadata is for the sync adapter framework. The +android:resource + element specifies the name of the metadata file. +

+

+ 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. +

diff --git a/docs/html/training/sync-adapters/index.jd b/docs/html/training/sync-adapters/index.jd new file mode 100644 index 0000000000000..1f7977b2d4fcc --- /dev/null +++ b/docs/html/training/sync-adapters/index.jd @@ -0,0 +1,135 @@ +page.title=Transferring Data Using Sync Adapters + +trainingnavtop=true +startpage=true + + +@jd:body + +
+
+ +

Dependencies and prerequisites

+ + +

You should also read

+ + +

Try it out

+ +
+ Download the sample +

BasicSyncAdapter.zip

+
+ +
+
+

+ 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. +

+

+ 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: +

+
+
+ Plug-in architecture +
+
+ Allows you to add data transfer code to the system in the form of callable components. +
+
+ Automated execution +
+
+ 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. +
+
+ Automated network checking +
+
+ The system only runs your data transfer when the device has network connectivity. +
+
+ Improved battery performance +
+
+ 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. +
+
+ Account management and authentication +
+
+ If your app requires user credentials or server login, you can optionally + integrate account management and authentication into your data transfer. +
+
+

+ 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. +

+

+ Note: 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}. +

+

Lessons

+
+
+ Creating a Stub Authenticator +
+
+ 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. +
+
+ Creating a Stub Content Provider +
+
+ 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. +
+
+ Creating a Sync Adapter +
+
+ Learn how to encapsulate your data transfer code in a component that the sync + adapter framework can run automatically. +
+
+ Running a Sync Adapter +
+
+ Learn how to trigger and schedule data transfers using the sync adapter framework. +
+
diff --git a/docs/html/training/sync-adapters/running-sync-adapter.jd b/docs/html/training/sync-adapters/running-sync-adapter.jd new file mode 100644 index 0000000000000..8fb7e80ce5bd9 --- /dev/null +++ b/docs/html/training/sync-adapters/running-sync-adapter.jd @@ -0,0 +1,524 @@ +page.title=Running a Sync Adapter + +trainingnavtop=true +@jd:body + + +
+
+ +

This lesson teaches you how to:

+
    +
  1. Run the Sync Adapter When Server Data Changes +
  2. Run the Sync Adapter When Content Provider Data Changes
  3. +
  4. Run the Sync Adapter After a Network Message
  5. +
  6. Run the Sync Adapter Periodically
  7. +
  8. Run the Sync Adapter On Demand
  9. +
+ + +

You should also read

+ + +

Try it out

+ +
+ Download the sample +

BasicSyncAdapter.zip

+
+ +
+
+

+ 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. +

+

+ 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. +

+

+ You have the following options for running your sync adapter: +

+
+
+ When server data changes +
+
+ 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. +
+
When device data changes
+
+ 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. +
+
+ When the system sends out a network message +
+
+ 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. +
+
+ At regular intervals +
+
+ Run a sync adapter after the expiration of an interval you choose, or run it at a certain + time every day. +
+
On demand
+
+ 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. +
+
+

+ The rest of this lesson describes each of the options in more detail. +

+

Run the Sync Adapter When Server Data Changes

+

+ 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. +

+

+ Google Cloud Messaging (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. +

+

+ Note: 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. +

+ The following code snippet shows you how to run + {@link android.content.ContentResolver#requestSync requestSync()} in response to an + incoming GCM message: +

+
+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);
+            ...
+        }
+        ...
+    }
+    ...
+}
+
+

Run the Sync Adapter When Content Provider Data Changes

+

+ 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. +

+

+ Note: 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. +

+

+ 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. +

+

+ 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. +

+ +

+ 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: +

+
+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);
+        ...
+    }
+    ...
+}
+
+

Run the Sync Adapter After a Network Message

+

+ 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. +

+

+ 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. +

+

+ 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()}. +

+

+ 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: +

+
+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);
+        ...
+    }
+    ...
+}
+
+

Run the Sync Adapter Periodically

+

+ 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. +

+

+ 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. +

+

+ 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. +

+

+ 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. +

+

+ 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. +

+

+ 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()}. +

+

+ The following code snippet shows you how to schedule periodic sync adapter runs: +

+
+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);
+        ...
+    }
+    ...
+}
+
+

Run the Sync Adapter On Demand

+

+ 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. +

+

+ 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. +

+

+ 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()}. +

+

+ Run on demand transfers with the following flags: +

+
+
+ {@link android.content.ContentResolver#SYNC_EXTRAS_MANUAL SYNC_EXTRAS_MANUAL} +
+
+ Forces a manual sync. The sync adapter framework ignores the existing settings, + such as the flag set by {@link android.content.ContentResolver#setSyncAutomatically + setSyncAutomatically()}. +
+
+ {@link android.content.ContentResolver#SYNC_EXTRAS_EXPEDITED SYNC_EXTRAS_EXPEDITED} +
+
+ 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. +
+
+

+ The following code snippet shows you how to call + {@link android.content.ContentResolver#requestSync requestSync()} in response to a button + click: +

+
+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);
+    }
+
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 58db404e8133d..cb5775288b9b9 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -475,6 +475,37 @@ + +