am c355a1c1: DOC: Sync Adapters Android Training
* commit 'c355a1c156ef549f97e3dc6611ab2a9c484889c5': DOC: Sync Adapters Android Training
This commit is contained in:
BIN
docs/downloads/training/BasicSyncAdapter.zip
Normal file
BIN
docs/downloads/training/BasicSyncAdapter.zip
Normal file
Binary file not shown.
290
docs/html/training/sync-adapters/creating-authenticator.jd
Normal file
290
docs/html/training/sync-adapters/creating-authenticator.jd
Normal file
@@ -0,0 +1,290 @@
|
||||
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>
|
||||
203
docs/html/training/sync-adapters/creating-stub-provider.jd
Normal file
203
docs/html/training/sync-adapters/creating-stub-provider.jd
Normal file
@@ -0,0 +1,203 @@
|
||||
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>
|
||||
658
docs/html/training/sync-adapters/creating-sync-adapter.jd
Normal file
658
docs/html/training/sync-adapters/creating-sync-adapter.jd
Normal file
@@ -0,0 +1,658 @@
|
||||
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>
|
||||
135
docs/html/training/sync-adapters/index.jd
Normal file
135
docs/html/training/sync-adapters/index.jd
Normal file
@@ -0,0 +1,135 @@
|
||||
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>
|
||||
524
docs/html/training/sync-adapters/running-sync-adapter.jd
Normal file
524
docs/html/training/sync-adapters/running-sync-adapter.jd
Normal file
@@ -0,0 +1,524 @@
|
||||
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>
|
||||
@@ -475,6 +475,37 @@
|
||||
</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 -->
|
||||
|
||||
Reference in New Issue
Block a user