注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

原文链接:http://developer.android.com/training/sync-adapters/creating-sync-adapter.html


在你应用中的同步适配器组件会封装在设备和服务器之间传输数据的任务代码。基于你提供的调度和触发器,同步适配器框架会在同步适配器组件中运行你的代码。要将同步适配组件添加到你的应用,你需要添加下列部件:

同步适配器类

这是一个将你的数据传输代码封装到一个接口中,该接口与同步适配器框架兼容。

捆绑Service

一个组件,它可以允许同步适配器框架在你的同步适配器类中运行代码。

同步适配器的XML元数据文件

一个文件,包含了你的同步适配器信息。框架会读取该文件并确定应该如何加载并调度你的数据传输任务。

在应用清单文件的声明

在XML文件中声明的捆绑服务,并指出同步适配器的元数据。

这节课将会向你展示如何定义这些元素。


一). 创建一个同步适配器类

在这部分课程中,你将会学习如何创建同步适配器类,该类封装了数据传输的代码。创建该类并包含继承的同步适配器的基类,为该类定义构造函数,并实现你定义的数据传输任务的方法。

继承同步适配器基类:AbstractThreadedSyncAdapter

要创建同步适配器组件,首先继承AbstractThreadedSyncAdapter,然后编写它的构造函数。每次你的同步适配器组件创建的时候,构造函数就会执行配置任务,和你使用Activity.onCreate()配置Activity是一样的。例如,如果你的应用使用一个内容提供器来存储数据,那么使用构造函数来获取一个ContentResolver实例。由于从Android 3.0开始添加了第二种形式的构造函数,来支持parallelSyncs参数,所以你需要创建两种形式的构造函数来保证兼容性。

Note:

同步适配器框架是设计成和同步适配器组件的单例一起工作的。实例化同步适配器组件的更多细节,可以阅读:Bind the Sync Adapter to the Framework。 

下面的代码展示了如何实现AbstractThreadedSyncAdapter和它的构造函数:

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

在onPerformSync()中添加数据传输代码

同步适配器组件并不会自动地执行数据传输。相反地,它只是对你的数据传输代码进行封装,所以同步适配器框架可以在后台执行数据传输,而不会涉及到你的应用。当框架准备同步你的应用数据时,它会调用你的onPerformSync()方法的实现。

为了便于将你的数据从你的应用程序代码转移到同步适配器组件,同步适配器框架调用onPerformSync(),它具有下面的参数:

账户(Account)

Account对象是和激活同步适配器的事件相关联的。如果你的服务不需要使用账户,你不需要使用这个对象内的信息。

额外数据(Extras)

一个Bundle对象,它包含了激活同步适配器的时间所具有的的标识。

权威(Authority)

系统内容提供器的权威。你的应用必须要有访问它的权限。通常,权威对应于你应用的内容提供器。

内容提供器客户端(Content provider client)

一个内容提供器的ContentProviderClient对象是由权威参数所指定的。一个ContentProviderClient是一个内容提供器的轻量级共有接口。它的基本功能和一个ContentResolver一样。如果你正在使用一个内容提供器来存储你的应用的数据,你可以用该对象和提供器连接。否则的话你可以忽略它。

同步结果(Sync result)

一个SyncResult对象,你可以使用它来将信息发送到同步适配器框架。

下面的代码片段展示了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.
*/
...
}

但是实际的onPerformSync()实现是要根据你的应用数据的同步需求以及服务器的连接协议来制定的,有一些你应该要实现的基本任务,如下所示:

连接到一个服务器

尽管你可以假定当你开始传输数据时,可以获取到网络连接,但是同步适配器框架并不会自动地连接到一个服务器。

下载和上传数据

一个同步适配器不会自动执行数据传输。如果你想要从一个服务器下载数据并将它存储到一个内容提供器中,你必须提供请求数据,下载数据和将数据插入到提供器里的代码。同样地,如果你想把数据发送到一个服务器,你必须要从一个文件,数据库或者提供器中读取数据,并且发送必须的上传请求。你也需要处理在你执行数据传输时所发生的网络错误。

处理数据冲突或者确定当前的数据是怎样的

一个同步适配器不会自动地解决服务器数据与设备数据的冲突。同时,它也不会检测服务器上的数据是否比设备上的数据要新,反之亦然。因此,你必须提供处理此状况的算法。

清理

在数据传输的尾声,记得要关闭网络连接,清除临时文件盒缓存。

Note:

同步适配器框架在一个后台线程中执行onPerformSync()方法,所以你不需要配置你自己的后台处理任务。

另外,你应该尝试将你的定期网络相关的任务结合起来,并将它们添加到onPerformSync()中。通过将所有网络任务集中到该方法中,你可以节省由启动和停止网络接口所造成的电量损失。有关更多如何在进行网络访问时更高效地使用电池,可以阅读:Transferring Data Without Draining the Battery(博客链接:http://www.cnblogs.com/jdneo/p/3620713.html),它描述了一些你的数据传输代码可以包含的网络访问任务。


二). 将同步适配器和框架进行绑定

你现在在一个同步适配器框架中已经封装了你的数据传输代码,但是你必须向框架提供你的代码。为了做这一点,你需要创建一个捆绑Service,它将一个特殊的Android binder对象从同步适配器组件传递给框架。有了这一binder对象,框架可以激活onPerformSync()方法并将数据传递给binder对象。

在你的服务的onCreate()方法中将你的同步适配器组件实例化为一个单例。通过在onCreate()方法中实例化该组件,你可以延迟到服务启动后再创建它,这会在框架第一次尝试执行你的数据传输时发生。你需要通过一种线程安全的方法来实例化组件,防止同步适配器框架会将你的同步适配器对于激活和调度的响应排成多个执行队列。

作为例子,下面的代码片段展示了你应该如何创建一个捆绑Service的类的实现,实例化你的同步适配器组件,并获取Android binder对象:

package com.example.android.syncadapter;
/**
* Define a Service that returns an 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:

要看更多同步适配器的捆绑服务的例子,可以阅读样例代码。


三). 添加框架所需的账户

同步适配器框架需要每个同步适配器拥有一个账户类型。在Add the Authenticator Metadata File章节中,你声明了账户类型的值。现在你需要在Android系统中配置该账户类型。要配置账户类型,通过调用addAccountExplicitly()添加一个假的账户并使用其账户类型。

最佳的调用该方法的地方是在你的应用的启动Activity的onCreate()方法中。下面的代码样例展示了你应该如何做:

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

四). 添加同步适配器的元数据文件

要将你的同步适配器组件添加到框架中,你需要向框架提供描述组件的元数据,以及额外的标识信息。元数据指定了你未你的同步适配器所创建的账户类型,声明了一个和你的应用相关联的内容提供器权威,对和同步适配器相关的一部分系统用户接口进行控制,并声明了其它同步相关的标识。在你的应用工程中的“/res/xml/”目录下的一个特定的文件内声明这一元数据,你可以为这个文件任意起一个名字,不过通常都叫做:“syncadapter.xml”。

在这一文件中包含了一个单一的XML元素“<sync-adapter>”,并且它包含了下列的属性字段:

android:contentAuthority

你的内容提供器的URI权威。如果你在前一节课程中(博客链接:http://www.cnblogs.com/jdneo/p/3654420.html)为你的应用创建了一个置空的内容提供器,使用你在清单文件中添加的<provider>标签内的android:authorities属性的值。这一属性的更多细节在章节Declare the Provider in the Manifest(博客链接:http://www.cnblogs.com/jdneo/p/3655747.html)中有更多的介绍。

如果你正在使用同步适配器,从内容提供器将数据传输到服务器,这个值应该和你的数据的URI权威的值是一样的。这个值也是你在清单文件中添加的<provider>标签内的android:authorities属性的值。

android:accountType

同步适配器框架所需要的账户类型。这个值必须和你创建验证器的元数据文件中所提供的一致(详细内容可以阅读:Add the Authenticator Metadata File,博客链接:http://www.cnblogs.com/jdneo/p/3655747.html)。这也是你在上一节中代码片段里的常量“ACCOUNT_TYPE”的值。

配置相关属性

android:userVisible

指的是同步适配器框架所需要的账户类型。默认地,和账户类型相关联的账户图标和标签在系统的设置中的账户选项中可以看见,所以你需要将你的同步适配器对用户不可见,除非你拥有一个账户类型或者账户类型,可以轻松地和你的应用相关联。如果你将你的账户类型设置为不可见,你仍然可以允许用户通过你的应用的一个activity内的用户接口来控制你的同步适配器。

android:supportsUploading

允许你将数据上传到云。如果你的应用仅仅下载数据,那么设置为“false”。

android:allowParallelSyncs

允许在同一时间你的同步适配器组件的多个实例运行。如果你的应用支持多个用户账户并且你希望多个用户并行地传输数据,那么使用这个属性。如果你从不执行多个数据传输,这个标识是没用的。

android:isAlwaysSyncable

指明同步适配器框架可以在任何你指定的时间运行你的永不适配器。如果你希望通过代码来控制同步适配器的运行,将这个标识设置为“false”,然后调用requestSync()来执行同步适配器。要学习更多关于运行一个同步适配器的知识,可以阅读:Running a Sync Adapter

下面的例子展示了一个同步适配器的XML,使用了一个假的账户并只进行下载:

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

五). 在清单文件中声明同步适配器

一旦你将同步适配器组件添加到了你的应用中,你需要声明相关的权限来使用它,并且你需要声明你所添加的捆绑Service

由于同步适配器组件运行网络与设备之间传输数据的代码,你需要使用网络的权限。另外,你的应用需要权限来读写同步适配器的配置信息,这样你才能通过你应用中的其它组件去控制同步适配器。你还需要一个特殊的权限允许你的应用使用你在Creating a Stub Authenticator(博客链接:http://www.cnblogs.com/jdneo/p/3654420.html)中所创建的验证器组件。

要请求这些权限,将下列内容添加到你的应用清单文件中,并作为<manifest>标签的子标签:

android.permission.INTERNET

允许同步适配器访问网络,一致于它可以从设备下载和上传数据到服务器。如果之前请求了该权限,那么你就不需要重复请求了。

android.permission.READ_SYNC_SETTINGS

允许你的应用读取当前的同步适配器配置。例如,你需要该权限来调用getIsSyncable()

android.permission.WRITE_SYNC_SETTINGS

允许你的应用对同步适配器的配置进行控制。你需要这一权限来执行addPeriodicSync(),以此设置执行同步的时间间隔。调用requestSync()需要用到该权限。更多信息可以阅读:Running A Sync Adapter

android.permission.AUTHENTICATE_ACCOUNTS

允许你使用在Creating a Stub Authenticator(博客链接:http://www.cnblogs.com/jdneo/p/3654420.html)中所创建的验证器组件。

下面的代码片段展示了如何添加权限:

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

最后,要声明框架使用的捆绑Service和你的同步适配器进行交互,添加下列的XML代码到你的应用清单文件中,作为<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>

<intent-filter>标签配置了一个过滤器,它会被带有“android.content.SyncAdapter”这一actionintent所激活,而这一intent一般是由系统为了运行同步适配器而发出的。当过滤器被激活时,系统会启动你所创建的捆绑服务,在例子中它叫做“SyncService”。属性android:exported="true"允许你应用之外的其它进程(包括系统)访问这一Service。属性android:process=":sync"告诉系统在一个全局共享,且称之为“sync”的进程内运行Service。如果你的应用中有多个同步适配器,那么它们可以共享该进程,这有助于减少开销。

<meta-data>标签提供了你之前为同步适配器所创建的元数据文件。属性android:name指出这一元数据是针对于同步适配器框架的。而android:resource标签则指定了元数据文件的文字。

现在你拥有了所有同步适配器的相关组件。下一节课将讲授如何让同步适配器框架运行你的同步适配器,既可以通过响应一个事件的方式,也可以通过执行一个定期任务调度的方式。

【Android Developers Training】 95. 创建一个同步适配器的更多相关文章

  1. 【Android Developers Training】 92. 序言:使用同步适配器传输数据

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  2. 【Android Developers Training】 94. 创建一个空内容提供器(Content Provider)

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  3. 【Android Developers Training】 93. 创建一个空验证器

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  4. 【Android Developers Training】 96. 运行一个同步适配器

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  5. 【Android Developers Training】 1. 创建一个Android项目工程

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  6. 【Android Developers Training】 18. 重新创建一个Activity

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  7. 【Android Developers Training】 21. 创建一个可变动的UI

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  8. 【Android Developers Training】 20. 创建一个Fragment

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  9. 【Android Developers Training】 105. 显示一个位置地址

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

随机推荐

  1. [刷题]算法竞赛入门经典 3-7/UVa1368 3-8/UVa202 3-9/UVa10340

    书上具体所有题目:http://pan.baidu.com/s/1hssH0KO 都是<算法竞赛入门经典(第二版)>的题目,标题上没写(第二版) 题目:算法竞赛入门经典 3-7/UVa13 ...

  2. MySQL 的性能(上篇)—— SQL 执行时间分析

    简介 文中内容均为阅读前辈的文章所整理而来,参考文章已在最后全指明 本文分为上下两篇: 上篇:MySQL 的 SQL 执行时间分析 下篇:MySQL 性能优化 后端开发必然会接触到数据库,数据层的优劣 ...

  3. 【转】jQuery Validate验证框架详解

    jQuery校验官网地址:http://bassistance.de/jquery-plugins/jquery-plugin-validation 一.导入js库 <script type=& ...

  4. .net操作InI文件

    public class INI { public static string IniFileName = "";//路径 [DllImport("kernel32&qu ...

  5. javaWeb学习总结(10)- EL表达式

    一.EL表达式简介 EL 全名为Expression Language.EL主要作用: 1.获取数据 EL表达式主要用于替换JSP页面中的脚本表达式,以从各种类型的web域 中检索java对象.获取数 ...

  6. Ubuntu上配置SQL Server Always On Availability Group(Configure Always On Availability Group for SQL Server on Ubuntu)

    下面简单介绍一下如何在Ubuntu上一步一步创建一个SQL Server AG(Always On Availability Group),以及配置过程中遇到的坑的填充方法. 目前在Linux上可以搭 ...

  7. R语言的导数计算(转)

    转自:http://blog.fens.me/r-math-derivative/ 前言 高等数学是每个大学生都要学习的一门数学基础课,同时也可能是考完试后最容易忘记的一门知识.我在学习高数的时候绞尽 ...

  8. js设置,获取,删除Cookie

    //JS操作cookies方法! //写cookies function setCookie(name,value) {     var Days = 30;     var exp = new Da ...

  9. poj3207

    poj3207 题意 平面上,一个圆,圆的边上按顺时针放着n个点.现在要连m条边, 比如a,b,那么a到b可以从圆的内部连接,也可以从圆的外部连接. 给你的信息中,每个点最多只会连接的一条边.问能不能 ...

  10. WEB前端:浏览器(IE+Chrome+Firefox)常见兼容问题处理--03

    兼容问题目录 16.IE67下子级有相对定位,并且比父级要大.那父级overflow:hidden;后是包不住它的 17.IE6下同一层级的浮动元素会盖住绝对定位元素 18.IE6下定位父级的宽高是奇 ...