联系方式供应z者
ContactsContract.Contacts
ContactsContract.RawContacts
ContactsContract.Data
android.provider.ContactsContract.StreamItems
相关样本
联系人管理器
样品同步适配器
也可以看看
内容提供商基础
联系人提供商是管理设备的关于人的数据中心存储库一个强大而灵活的Andr​​oid组件。联系人提供商是您在设备的联系人应用程序中看到的数据源,并且您还可以在设备和网络服务之间的访问自己的应用程序和数据传输数据。提供者容纳一个广泛的数据源的,并试图管理尽可能多的数据可能对于每个人,其结果是它的组织是复杂的。正因为如此,供应商的API包括一系列的合同类和有利于双方的数据检索和修改接口。


本指南介绍以下内容:


其基本结构供应商。

如何检索从提供的数据。


如何修改提供的数据。
如何写一个同步适配器从服务器到联系人提供商同步数据。
本指南假定您知道Android的内容提供商的基础知识。要了解更多关于Android的内容提供商,阅读内容提供商基本指南。样本同步适配器示例应用程序是使用同步适配器联系人提供和由谷歌Web服务托管的一个示例应用程序之间传输数据的一个例子。


联系方式供应商组织


联系人提供者是一个Android内容提供商组成部分。它保持关于一个人三种类型的数据,其每一个对应于由提供者提供,如在图1中所示的表:


图1.联系人提供商表结构。


这三个表是由他们的合同类的名称通常称为。类定义的URI的内容,列名,并通过使用表列值的常数:


ContactsContract.Contacts表
行代表不同的人,根据原料接触的行聚集。
ContactsContract.RawContacts表
含有一个人的数据的特定用户帐户和类型的摘要,行。
ContactsContract.Data表
含有原料接触的细节,诸如电子邮件地址或电话号码的行。
合同类ContactsContract代表的其他表是联系人提供用于管理其操作或支持该设备的联系人或电话应用程序的特定功能辅助表。


生接触


一个原始的接触代表单个帐户类型和帐户名称来一个人的数据。由于联系人提供商允许一个以上的在线服务数据的一个人的来源,联系人提供商允许为同一人多次接触原料。多生交往还允许用户将个人数据从多个帐户从同一账户类型相结合。


最为原始接触的数据都没有存储在ContactsContract.RawContacts表。相反,它存储在ContactsContract.Data表的一个或多个行。每个数据行都有一个包含其母公司ContactsContract.RawContacts行的RawContacts._ID值的列Data.RAW_CONTACT_ID。


重要原料接触列


在ContactsContract.RawContacts表中的重要列列于表1。请仔细阅读表后,下面的注意事项:


表1.重要原料接触列。

列名使用注意事项
ACCOUNT_NAME的帐户类型这是此生接触的来源帐户名。例如,谷歌帐户的帐户名是设备所有者的Gmail地址之一。请参阅帐户类型的详细信息下一个条目。这个名字的格式是具体到其帐户类型。它不一定是电子邮件地址。
ACCOUNT_TYPE帐户类型这是此生接触的来源。例如,谷歌帐户的帐户类型是com.google。永远与你自己的域或控制域标识符合您的帐户类型。这将确保您的帐户类型是独一无二的。通常提供的联系人数据的帐户类型具有与联系人同步提供相关的同步适配器。
删除了“已删除”标志的原始接触。此标志允许联系人提供商保持内部行,直到同步适配器能够从他们的服务器中删除的行,然后最终删除存储库中的行。

笔记


以下是有关ContactsContract.RawContacts表重要提示:


一个原始联系人的姓名没有存储在其ContactsContract.RawContacts一行。相反,它存储在ContactsContract.Data表,在一个ContactsContract.CommonDataKinds.StructuredName行。一个原始联系人只有一行这种类型的ContactsContract.Data表。
注意:要在原始接触一行使用自己的帐户数据,必须先用的AccountManager注册。要做到这一点,提示用户帐户类型和他们的帐户名称添加到帐户列表。如果你不这样做,联系人提供商将自动删除您的原始联系行。
例如,如果你希望你的应用程序保持接触数据与域com.example.dataservice基于Web的服务,用户的帐号为您服务是becky.sharp@dataservice.example.com,用户必须先添加您的应用程序之前的账户“类型”(com.example.dataservice)和账户“名”(becky.smart@dataservice.example.com)可以加生接触的行。您可以在文档中解释这一要求给用户,或者你可以提示用户添加的种类和名称,或两者兼而有之。帐户类型和帐户名称以更详细的下一节描述。
原始接触数据来源


要了解如何接触生的工作,考虑到用户的“艾米莉·狄金森”谁拥有她的设备上定义以下三个用户帐户:


emily.dickinson@gmail.com
emilyd@gmail.com
Twitter帐户“belle_of_amherst”
该用户已启用的帐户设置,所有这三个帐户同步联系人。


假设艾米莉狄金森打开浏览器窗口,登录到Gmail作为emily.dickinson@gmail.com,打开联系人,并增加了“托马斯·希金森”。后来,她登录到Gmail作为emilyd@gmail.com并发送电子邮件至“托马斯希金森”,它会自动添加他为联系人。她还遵循“colonel_tom”在Twitter(托马斯·希金森的Twitter ID)。


联系人提供商创建三个原始联系人作为这项工作的结果:


对于“托马斯·希金森”A原接触emily.dickinson@gmail.com有关。用户帐户类型是谷歌。
对于“托马斯·希金森”第二生接触emilyd@gmail.com有关。用户帐户类型也是谷歌。有即使名称是相同的一个以前的名称,因为对于不同的用户帐户中加入人的第二生接触。
对于“托马斯·希金森”与“belle_of_amherst”有关的第三个原料接触。用户帐户类型是Twitter的。

数据


如前面所指出的,对于一个原始联系人的数据被存储在链接到原始接触的_ID值ContactsContract.Data行。这允许单个原料接触到具有相同的数据类型的多个实例,例如电子邮件地址或电话号码。例如,如果“托马斯·希金森”对于emilyd@gmail.com(与谷歌帐户emilyd@gmail.com相关托马斯·希金森原料接触行)具有thigg@gmail.com的家庭电子邮件地址和的工作电子邮件地址thomas.higginson@gmail.com,联系人提供商存储两个电子邮件地址,行并将它们都链接到原始的接触。


注意,不同类型的数据被存储在该单个表。显示姓名,电话号码,电子邮件,邮寄​​地址,照片和网站的细节行中的ContactsContract.Data表中的所有发现。为了帮助实现这一点,ContactsContract.Data表中有描述性的名字,并与通用名称等一些列。一个描述名称列的内容具有相同的含义而不管该行中的数据的类型,而通用名栏的内容具有取决于数据的类型不同的含义。


描述列名


描述的列名的一些实例是:


RAW_CONTACT_ID
此数据的原始接触的_ID列的值。
MIMETYPE
类型存储在该行中的数据,表示为自定义的MIME类型。在联系人提供程序使用ContactsContract.CommonDataKinds的子类中定义的MIME类型。这些MIME类型是开源的,并且可以通过与联系人提供商合作的任何应用程序或同步适配器一起使用。
IS_PRIMARY
如果这种类型的数据行的,就可能出现一次以上的原料接触时,IS_PRIMARY列标志包含该类型的主数据中的数据行。例如,如果用户长时间按下一个电话号码供一个接触,并选择设置默认,则含有该号码的ContactsContract.Data行都有IS_PRIMARY列设置为非零值。
通用列名


有迹象表明,一般可通过DATA15名为DATA1 15通用列和只应由同步适配器一起使用的另外四个通用列SYNC1通过SYNC4。通用列名常量总是工作,无论行包含数据的类型。


数据1列索引。联系人提供始终使用此列,该提供者期望将一个查询的最频繁的目标的数据。例如,电子邮件行,该列包含实际的电子邮件地址。


按照惯例,该列DATA15被保留用于存储二进制大对象(BLOB)数据如照片缩略图。


特定类型的列名


以促进与特定类型的行的列工作,联系人提供程序还提供了类型特异性的列名的常数,在ContactsContract.CommonDataKinds的子类所定义。常量只是给予不同的常量名以相同的列名,它可以帮助您在特定类型的一行访问数据。


例如,ContactsContract.CommonDataKinds.Email类定义特定类型的列名常量具有MIME类型Email.CONTENT_ITEM_TYPE一​​个ContactsContract.Data一行。该类包含电子邮件地址列中的常量地址。地址的实际值是“DATA1”,这是相同的列的总称。


注意:不要使用有供应商的预定义的MIME类型中的一种一排自己的自定义数据添加到ContactsContract.Data表。如果你这样做,可能会丢失数据或导致供应出现故障。例如,你不应该包含用户名,而不是在列DATA1的电子邮件地址的MIME类型Email.CONTENT_ITEM_TYPE添加行。如果您使用自己的自定义MIME类型的行,然后你可以自由定义自己的特定类型的列名,并按照自己的喜好使用的列。


图2显示了列和数据列如何描述出现在ContactsContract.Data行,怎么特定类型的列名“叠加”的通用列名


图2.特定于类型的列名和通用列名。


特定类型的列名班


表2列出了最常用的类型特定的列名的类:


表2.特定于类型的列名班
数据说明的映射类类型
ContactsContract.CommonDataKinds.StructuredName名称数据与此数据列相关的原料接触。一个原始联系人只有这些行之一。
ContactsContract.CommonDataKinds.Photo主要的照片与此数据行相关的原料接触。一个原始联系人只有这些行之一。
ContactsContract.CommonDataKinds.Email的电子邮件地址与此数据行相关的原料接触。的裸联系人可以有多个电子邮件地址。
ContactsContract.CommonDataKinds.StructuredPostal邮政地址与此数据行相关的原料接触。的裸联系人可以有多个邮政地址。
ContactsContract.CommonDataKinds.GroupMembership链接的原始接触以在联系人提供的组中的一个的标识符。组是一个帐户类型和帐户名的可选功能。他们在更详细的部分联系人组描述。

往来


联系人提供结合了所有帐户类型和帐户名称原料接触行形成接触。这便于显示和修改所有用户已收集的人的数据。联系人管理供应商创造新的联系人行,和生的联系人与现有联系人行聚集。无论是应用还是同步适配器被允许添加联系人,并在接触一些列的列是只读的。


注意:如果您尝试添加联系人到联系人提供与INSERT(),你会得到一个UnsupportedOperationException异常。如果您尝试更新的列为列“只读”的更新将被忽略。


联系人提供商创建响应增加了一个新的原始联系人不匹配任何现有的接触一个新的联系人。提供者也确实本如果以这样的方式现有原始联系人的数据的变化,它不再接触的匹配其所先前连接。如果一个应用程序或同步适配器创建一个新的原始接触,做匹配现有的接触,新的原始联系人汇集到现有的联系人。


联系人提供链接的联系人行其原料的接触与排在联系人表中的联系人行的_ID列。原料接触表ContactsContract.RawContacts的CONTACT_ID列包含与每个原始联系人行相关联的联系人行_ID值。


该ContactsContract.Contacts表还具有列LOOKUP_KEY这是一个“永久”链接到联系人行。由于联系人提供商自动保持联系,它可以响应聚合或同步更改联系人行的_ID价值。即使发生这种情况,内容URI CONTENT_LOOKUP_URI接触的LOOKUP_KEY组合仍然将指向接触行,所以你可以使用LOOKUP_KEY保有环节为“最爱”的接触,等等。此列有它自己的格式是无关的_ID列的格式。


图3显示了三个主要的表是如何相互关联。


图3.联系人,原始联系人和详细信息表之间的关系。


从数据同步适配器


用户输入联系人的数据直接进入设备,但数据还流入从Web服务通过同步适配器,它自动化设备和服务之间的数据传输联系人提供商。同步适配器在系统的控制下在后台运行,并且他们叫ContentResolver的方法来管理数据。


在Android中,一个同步适配器适用于由账户类型确定的Web服务。每个同步适配器一个帐户类型的作品,但它可以支持多个帐户名该类型。帐户类型和帐户名称中的原始数据接触的部分来源是简要说明。以下定义提供更多的细节,描述了帐户类型和名称如何与同步适配器和服务。


帐户类型
标识该用户已经存储的数据的服务。大部分时间,用户的服务进行认证。例如,谷歌联系人是一个帐户类型,由代码google.com标识。此值对应于由的AccountManager使用的帐户类型。
用户名
标识帐户类型的特定帐户或登录。谷歌联系人账户是一样的谷歌账户,其中有一个电子邮件地址作为帐户名。其他服务可能会使用单字名或数字ID。
帐户类型不必是唯一的。用户可以配置多个谷歌帐户的联系人及其数据下载到联系人供应商;如果用户有一组用于个人帐户名个人联系人,和另一组的工作,这可能发生。账户名称通常是唯一的。在一起,它们识别联系人提供程序和外部服务之间的特定数据流。


如果你想你的服务的数据传输到联系人提供商,你需要编写自己的同步适配器。此进行更详细的部分联系人提供同步适配器说明。


图4示出了联系人提供如何适合关于人的数据流。在框中标记为“同步适配器”,每个适配器是由它的帐户类型标记。


图4.数据的联系人提供流动。


所需的权限


要访问联系人提供商必须请求以下权限的应用程序:


读给一个或多个表的访问
READ_CONTACTS,在AndroidManifest.xml与作为<使用许可权的android:NAME =“android.permission.READ_CONTACTS”>的<使用许可权>元素中指定。
写入到一个或多个表的访问
WRITE_CONTACTS,在AndroidManifest.xml与作为<使用许可权的android:NAME =“android.permission.WRITE_CONTACTS”>的<使用许可权>元素中指定。
这些权限不延伸到用户配置文件数据。用户配置文件和其所需的权限是在以下部分,用户配置文件进行讨论。


请记住,用户的通讯录数据是个人和敏感。用户担心自己的隐私,所以他们不希望应用程序收集有关他们或他们的联系人数据。如果不是显而易见的,为什么你需要的权限访问他们的联系人数据,他们可以给你的应用程序收视率低或者干脆拒绝安装它。


用户配置文件


该ContactsContract.Contacts表具有包含该设备的用户简档数据的单一行。该数据描述了设备的用户,而不是用户的联系人中的一个。轮廓触头行链接到原始触点排为使用信息的每个系统。每个配置文件的原始接触行可以有多个数据行。用于访问用户配置文件中的常量的ContactsContract.Profile类是可用的。


访问用户配置文件需要特殊的权限。除了读取和写入所需的READ_CONTACTS和WRITE_CONTACTS权限,访问到用户配置文件需要读的android.Manifest.permission#READ_PROFILE和android.Manifest.permission#WRITE_PROFILE权限分别写访问。


请记住,你应该考虑用户的配置文件是敏感的。许可android.Manifest.permission#READ_PROFILE允许您访问设备用户的个人身份数据。请一定要告诉用户为什么你需要在你的应用程序的描述中用户配置文件的访问权限。


要检索包含用户的个人资料的联系人行,调用ContentResolver.query()。设置内容URI来CONTENT_URI,不提供任何选择标准。您也可以使用此内容的URI的基础URI用于检索的配置文件的原始接触或数据。例如,该片段检索配置文件数据:

// Sets the columns to retrieve for the user profilemProjection = new String[]  {    Profile._ID,    Profile.DISPLAY_NAME_PRIMARY,    Profile.LOOKUP_KEY,    Profile.PHOTO_THUMBNAIL_URI  };// Retrieves the profile from the Contacts ProvidermProfileCursor =    getContentResolver().query(        Profile.CONTENT_URI,        mProjection ,        null,        null,        null);
注意:如果您检索多个联系人行,并要确定是否其中之一就是用户配置文件,测试排IS USER_PROFILE列。这列被设置为“1”,如果接触是用户简档。
联系方式提供元数据
联系人提供商管理着跟踪接触数据状态的存储库中的数据。这是关于存储库的元数据存储在不同的地方,包括原始的联系人,数据和联系人表行的ContactsContract.Settings表和ContactsContract.SyncState表。下表显示了这些片元数据的效果:
表3中的元数据联系人提供商
表列值含义
ContactsContract.RawContacts脏“0” - 不是因为上次同步改变。被改变的设备上,并有标记的原始接触可以同步回服务器。该值由联系人提供商时,Android应用程序更新行自动设置。
修改原始联系人或数据表同步适配器应始终将字符串CALLER_IS_SYNCADAPTER追加到URI他们所使用的内容。这可以防止标记行作为脏的供应商。否则,同步适配器修饰似乎是本地修改和被发送到服务器,即使该服务器是变形例的来源。
“1” - 从去年的同步变化,需要将同步回服务器。
ContactsContract.RawContacts VERSION该行的版本号。联系人提供商自动递增该值,每当行或其相关数据的变化。
ContactsContract.Data DATA_VERSION该行的版本号。联系人提供商自动递增这个值每当数据行被改变。
ContactsContract.RawContacts SOURCE_ID唯一标识此生联系,在其被创建的帐户的字符串值。当一个同步适配器创建一个新的原料接触,此栏应设置为服务器的唯一ID为原料的接触。当一个Android应用程序创建一个新的原始联系人,应用程序应该将此列留空。这标志着,它应该在服务器上创建一个新的原始接触,并获得了SOURCE_ID值同步适配器。
特别是,源ID必须为每个帐户类型唯一的,应在整个同步稳定:
独特之处:一个账户必须有它自己的源ID每个生接触。如果不执行这一点,你就会导致联系人应用程序的问题。请注意,对于相同的帐户式两种原料接触可能具有相同的源ID。例如,原始接触“托马斯·希金森”为帐户emily.dickinson@gmail.com允许具有相同的源ID为原料接触“托马斯·希金森”为账户emilyd@gmail.com。
稳定:源ID是在线服务的数据的原始接触的永久组成部分。例如,如果用户从应用程序的设置,并重新同步清除电话簿存储,恢复的原始接触应具有相同的源ID作为前。如果不强制执行此,快捷方式将停止工作。
ContactsContract.Groups GROUP_VISIBLE“0” - 这个群组的联络不应该在Android应用程序的用户界面可见。此列与允许用户隐藏在某些群体的接触服务器的兼容性。
“1” - 这个群组的联络被允许在应用程序用户界面可见。
ContactsContract.Settings UNGROUPED_VISIBLE“0” - 对于这个帐户和帐户类型,不属于一组联系人是不可见的Andr​​oid应用程序的用户界面。默认情况下,如果没有其原始触点属于一个组(组成员资格的原接触是通过一个或多个ContactsContract.CommonDataKinds.GroupMembership行中ContactsContract.Data表中所示)的接触是不可见的。通过设置ContactsContract.Settings表行这个标志的账户类型和账号,您可以强制联系人没有组是可见的。一个使用此标志是表现出与不使用组服务器联系。
“1” - 对于此帐户和帐户类型,即不属于一组联系人是应用的用户界面中可见。
ContactsContract.SyncState(全部)使用此表来存储元数据的同步适配器。与此表可以存储同步状态和其它同步相关

联系方式供应商访问


本节介绍从联系人提供访问数据,重点对以下原则:


实体的查询。
批量修改。
检索和修改与意图。
数据的完整性。
从同步适配器进行修改也包括在部分联系人提供商同步适配器的详细信息。


查询实体


由于联系人提供的表中,用层级组织的,它往往是有用的检索行和所有链接到它的“子”行。例如,要显示一个人的所有信息,你可能要检索一个ContactsContract.RawContacts一行单一ContactsContract.Contacts行的所有ContactsContract.RawContacts行,或所有ContactsContract.CommonDataKinds.Email行。为了推动这项工作,联系人提供商提供了实体的结构,它像数据库表之间的联接。


实体就像一个从父表及其子表中选定列组成的表。当您查询一个实体,需要提供基于可从实体列投影和搜索条件。其结果是,包含光标包含该被检索每个子表行的一行。例如,如果您查询ContactsContract.Contacts.Entity联系人姓名和所有ContactsContract.CommonDataKinds.Email行针对该名称的所有原材料联系人,你回来包含一行每个ContactsContract.CommonDataKinds.Email行的光标。


实体简化查询。使用一个实体,你可以一次检索所有联系人或生接触接触的数据,而不必首先查询父表获得一个ID,然后不必查询子表与该ID。另外,联系人提供程序处理在一个事务,这确保了所检索的数据是内部一致的针对一个实体的查询。


注:通常实体不包含父和子表中的所有列。如果您尝试使用的列名,是不是在列名常量实体名单的工作,你会得到一个异常。


下面的代码片段展示了如何检索所有联系人的原始接触行。该片段是有两个活动,“主”和“细节”的一个更大的应用程序的一部分。主要活动显示联系人行的列表;当用户选择一个,该活动发送其ID到详细的活性。详细活动使用ContactsContract.Contacts.Entity从所有与所选联系人相关联的原始联系人显示所有数据行。

This snippet is taken from the "detail" activity:

...  /*  * Appends the entity path to the URI. In the case of the Contacts Provider, the  * expected URI is content://com.google.contacts/#/entity (# is the ID value).  */  mContactUri = Uri.withAppendedPath(      mContactUri,      ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);  // Initializes the loader identified by LOADER_ID.  getLoaderManager().initLoader(      LOADER_ID, // The identifier of the loader to initialize      null,    // Arguments for the loader (in this case, none)      this);   // The context of the activity  // Creates a new cursor adapter to attach to the list view  mCursorAdapter = new SimpleCursorAdapter(      this,            // the context of the activity      R.layout.detail_list_item,  // the view item containing the detail widgets      mCursor,           // the backing cursor      mFromColumns,        // the columns in the cursor that provide the data      mToViews,          // the views in the view item that display the data      0);             // flags  // Sets the ListView's backing adapter.  mRawContactList.setAdapter(mCursorAdapter);...@Overridepublic Loader<Cursor> onCreateLoader(int id, Bundle args) {  /*  * Sets the columns to retrieve.  * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.  * DATA1 contains the first column in the data row (usually the most important one).  * MIMETYPE indicates the type of data in the data row.  */  String[] projection =    {      ContactsContract.Contacts.Entity.RAW_CONTACT_ID,      ContactsContract.Contacts.Entity.DATA1,      ContactsContract.Contacts.Entity.MIMETYPE    };  /*  * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw  * contact collated together.  */  String sortOrder =      ContactsContract.Contacts.Entity.RAW_CONTACT_ID +      " ASC";  /*  * Returns a new CursorLoader. The arguments are similar to  * ContentResolver.query(), except for the Context argument, which supplies the location of  * the ContentResolver to use.  */  return new CursorLoader(      getApplicationContext(), // The activity's context      mContactUri,       // The entity content URI for a single contact      projection,        // The columns to retrieve      null,           // Retrieve all the raw contacts and their data rows.      null,           //      sortOrder);        // Sort by the raw contact ID.}
当负载完成后,LoaderManager调用回调onLoadFinished()。之一的入参数这种方法是与查询结果的光标。在你自己的应用程序,你可以从该游标获取数据,以显示它或它的进一步工作。


批量修改


只要有可能,你应该插入,更新,并在“批处理模式”删除联系人提供的数据,通过创建ContentProviderOperation对象的ArrayList和调用applyBatch()。由于联系人提供执行在applyBatch()在一个事务中的所有操作,您的修改将永远不会离开联系人存储库处于不一致的状态。一个批量修改也方便插入,同时原始的接触及其明细数据。


注意:要修改单个原始接触,考虑派遣一个意图设备的联系人应用程序,而不是在你的应用程序处理的修改。这样做的更详细的部分检索与变形例与意图说明。


屈服点


含有大量运算的批量修改可以阻止其他进程,导致糟糕的整体用户体验。要组织所有你想在尽可能少的单独的列表尽可能地进行修改,并同时防止它们阻塞系统,你应该为一个或多个操作设置屈服点。屈服点是具有设置为true其isYieldAllowed()值ContentProviderOperation对象。当联系人提供者遇到的屈服点,将暂停其工作,让其他进程运行,关闭当前的事务。当提供者再次启动时,它继续在该ArrayList下一操作,并启动一个新的事务。


不导致每次调用多个交易applyBatch屈服点()。正因为如此,你应该为最后操作的屈服点为一组相关的行。例如,你应该在一组,增加了一个生接触行和它的相关联的数据的行,或最后一个操作的一组相关的一个单触点的行设置为最后一个动作的屈服点。


屈服点也是原子操作的一个单元。双屈服点之间的所有访问将成功或失败作为一个单元。如果不设置任何屈服点,最小的原子操作是操作的整批。如果你使用屈服点,可以防止降低系统性能的操作,而在同一时间,确保操作的子集是原子。


修改后向引用


当要插入一个新的原始接触行和其相关的数据行作为一组ContentProviderOperation对象,则有通过将原料接触的_ID值作为RAW_CONTACT_ID值到数据行到原始接触行链接。然而,当你创建的数据行的ContentProviderOperation,因为你还没有应用ContentProviderOperation为原料接触这行的值是不可用的。要解决这个问题,该ContentProviderOperation.Builder类有方法withValueBackReference()。这种方法允许你插入或前一个操作的结果修改列。


该withValueBackReference()方法有两个参数:



一个键值对的关键。此参数的值应该是在你修改表中的列名。
previousResult
值的ContentProviderResult对象从applyBatch数组中的从零开始的索引()。作为被施加了批量操作,每个操作的结果存储在结果中的中间阵列。的previousResult值是这些结果,这是检索并存储与键值之一的索引。这允许你插入一个新的原始联系人记录,并取回其_ID值,然后做一个“反向引用”的值当您添加ContactsContract.Data一行。
整个结果阵列创建当你第一次调用applyBatch(),其大小等于ContentProviderOperation的ArrayList的对象,您提供的大小。然而,结果数组中的所有元素都设置为null,如果你试图为一个尚未被应用,withValueBackReference操作()做了一回参考结果抛出异常。


下面演示了如何将分批一个新的原始联系人和数据。它们包括建立一个屈服点,并使用一回参考代码。该片段是createContacEntry()方法,该方法是在联系人管理器示例应用程序ContactAdder类的一部分的扩展版本。


第一个片段从UI检索联系人数据。此时,用户已经选择了应添加新的原始接触的帐户。

// Creates a contact entry from the current UI values, using the currently-selected account.protected void createContactEntry() {  /*  * Gets values from the UI  */  String name = mContactNameEditText.getText().toString();  String phone = mContactPhoneEditText.getText().toString();  String email = mContactEmailEditText.getText().toString();  int phoneType = mContactPhoneTypes.get(      mContactPhoneTypeSpinner.getSelectedItemPosition());  int emailType = mContactEmailTypes.get(      mContactEmailTypeSpinner.getSelectedItemPosition());
接下来的代码片断创建的操作将原始联系行插入到表ContactsContract.RawContacts:

 /*  * Prepares the batch operation for inserting a new raw contact and its data. Even if  * the Contacts Provider does not have any data for this person, you can't add a Contact,  * only a raw contact. The Contacts Provider will then add a Contact automatically.  */  // Creates a new array of ContentProviderOperation objects.  ArrayList<ContentProviderOperation> ops =      new ArrayList<ContentProviderOperation>();  /*  * Creates a new raw contact with its account type (server type) and account name  * (user's account). Remember that the display name is not stored in this row, but in a  * StructuredName data row. No other data is required.  */  ContentProviderOperation.Builder op =      ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)      .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())      .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());  // Builds the operation and adds it to the array of operations  ops.add(op.build());
接下来,该代码创建的显示名称,电话和电子邮件行的数据行。


每个操作建设者对象使用withValueBackReference()来获得RAW_CONTACT_ID。参考点回到从第一操作,这增加了生接触行并返回它的新_ID值ContentProviderResult对象。其结果是,每一个数据行被自动通过其RAW_CONTACT_ID链接到新ContactsContract.RawContacts行到它所属的。


,增加了电子邮件行ContentProviderOperation.Builder对象标记有withYieldAllowed(),它设置了一个屈服点:

// Creates the display name for the new raw contact, as a StructuredName data row.  op =      ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)      /*      * withValueBackReference sets the value of the first argument to the value of      * the ContentProviderResult indexed by the second argument. In this particular      * call, the raw contact ID column of the StructuredName data row is set to the      * value of the result returned by the first operation, which is the one that      * actually adds the raw contact row.      */      .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)      // Sets the data row's MIME type to StructuredName      .withValue(ContactsContract.Data.MIMETYPE,          ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)      // Sets the data row's display name to the name in the UI.      .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);  // Builds the operation and adds it to the array of operations  ops.add(op.build());  // Inserts the specified phone number and type as a Phone data row  op =      ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)      /*      * Sets the value of the raw contact id column to the new raw contact ID returned      * by the first operation in the batch.      */      .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)      // Sets the data row's MIME type to Phone      .withValue(ContactsContract.Data.MIMETYPE,          ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)      // Sets the phone number and type      .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)      .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);  // Builds the operation and adds it to the array of operations  ops.add(op.build());  // Inserts the specified email and type as a Phone data row  op =      ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)      /*      * Sets the value of the raw contact id column to the new raw contact ID returned      * by the first operation in the batch.      */      .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)      // Sets the data row's MIME type to Email      .withValue(ContactsContract.Data.MIMETYPE,          ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)      // Sets the email address and type      .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)      .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);  /*  * Demonstrates a yield point. At the end of this insert, the batch operation's thread  * will yield priority to other threads. Use after every set of operations that affect a  * single contact, to avoid degrading performance.  */  op.withYieldAllowed(true);  // Builds the operation and adds it to the array of operations  ops.add(op.build());
最后一个片段显示)中插入新的原始联系人和数据行调用applyBatch

// Ask the Contacts Provider to create a new contact  Log.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +      mSelectedAccount.getType() + ")");  Log.d(TAG,"Creating contact: " + name);  /*  * Applies the array of ContentProviderOperation objects in batch. The results are  * discarded.  */  try {      getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);  } catch (Exception e) {      // Display a warning      Context ctx = getApplicationContext();      CharSequence txt = getString(R.string.contactCreationFailure);      int duration = Toast.LENGTH_SHORT;      Toast toast = Toast.makeText(ctx, txt, duration);      toast.show();      // Log exception      Log.e(TAG, "Exception encountered while inserting contact: " + e);  }}
批处理操作还允许您实现乐观并发控制,应用修改事务,而不必锁定底层存储库的方法。要使用此方法,你申请的事务,然后检查是否有可能在同一时间尚未做出其他修改。如果发现发生了不一致的修改,你回滚事务,重试。
乐观并发控制是一个移动设备,其中有在同一时间只有一个用户,以及数据仓库同时访问有用的是罕见的。由于锁定未使用,没有时间浪费在设置锁或等待其他事务释放自己的锁。
要使用乐观并发控制,同时更新单个ContactsContract.RawContacts行,请按照下列步骤操作:
与您检索其他数据检索原始联系人的版本列。
创建适合强制约束,使用该方法newAssert查询(URI)一个ContentProviderOperation.Builder对象。对于内容的URI,使用RawContacts.CONTENT_URI与原料接触的_ID追加到它。
对于ContentProviderOperation.Builder对象,调用withValue()的版本列比较你刚才检索到的版本号。
对于相同ContentProviderOperation.Builder,调用withExpectedCount(),以确保只有一行被这个断言测试。
呼叫建立()创建ContentProviderOperation对象,然后传递给applyBatch ArrayList中添加此对象的第一个对象()。
应用批量交易。
如果原始联系行由你读了行,你试图去修改它的时间的时间之间的另一个操作更新的“断言”ContentProviderOperation会失败,整个操作批次将回退。然后,您可以选择重试批处理或采取一些其他动作。
下面的代码演示了如何在查询使用CursorLoader单一原料接触后建立一个“断言”ContentProviderOperation:

/** The application uses CursorLoader to query the raw contacts table. The system calls this method* when the load is finished.*/public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {  // Gets the raw contact's _ID and VERSION values  mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));  mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));}...// Sets up a Uri for the assert operationUri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);// Creates a builder for the assert operationContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);// Adds the assertions to the assert operation: checks the version and count of rows testedassertOp.withValue(SyncColumns.VERSION, mVersion);assertOp.withExpectedCount(1);// Creates an ArrayList to hold the ContentProviderOperation objectsArrayList ops = new ArrayList<ContentProviderOperationg>;ops.add(assertOp.build());// You would add the rest of your batch operations to "ops" here...// Applies the batch. If the assert fails, an Exception is throwntry  {    ContentProviderResult[] results =        getContentResolver().applyBatch(AUTHORITY, ops);  } catch (OperationApplicationException e) {    // Actions you want to take if the assert operation fails go here  }
检索和修改与意图


发送意图设备的联系人应用程序,您可以间接访问联系人提供商。其意图启动设备的联系人应用程序的用户界面,用户可以在里面做联系人相关的工作。有了这种类型的访问,用户可以:


挑选从列表中的联系人,并将它返回到您的应用程序做进一步的工作。
编辑现有的联系人数据。
插入任何其账户的新原料接触。
删除或多个联系人数据。
如果用户插入或更新的数据,你可以先收集数据并把它作为意图的一部分。


当您使用意图通过设备的联系人应用程序访问联系人提供者,你不必写自己的用户界面或代码访问的提供者。您还没有请求权限读取或写入提供商。该设备的联系人应用程序可以委托读取权限联系人给你,因为你通过其他应用程序进行修改,以提供者,你不必有写权限。


发送意图访问提供者的一般过程进行了详细的部分内容提供商基础指南中所述“意图通过数据访问。”动作,MIME类型和数据使用的可用任务值列于表4,而额外的值,可以用putExtra()为ContactsContract.Intents.Insert的参考文档中列出使用:


表4.联系方式供应商意图。
任务操作数据的MIME类型注意事项
挑选从列表ACTION_PICK其中的联系方式:
Contacts.CONTENT_URI,它显示的联系人列表。
Phone.CONTENT_URI,它显示了一个原始联系人的电话号码的列表。
StructuredPostal.CONTENT_URI,它显示了一个原始接触邮政地址的列表。
Email.CONTENT_URI,其中显示了原始联系人的电子邮件地址列表。
未使用的原始显示的联系人列表或数据从原始联系人列表,视所提供内容的URI类型。
调用startActivityForResult(),它返回选定行的内容URI。 URI的形式是表的内容URI与行的LOOKUP_ID追加到它。该设备的联系人应用程序代表读取和写入权限此内容URI为您的活动的使用寿命。详情请参见内容提供商基本指南。
插入一个新的原始接触Insert.ACTION N / A RawContacts.CONTENT_TYPE,MIME类型为一组原始的联系人。显示设备的联系人应用程序的添加联系人屏幕。显示你添加到意图的额外价值。如果用startActivityForResult()发送,新添加的原料接触的内容URI被传递回您的活动的的onActivityResult()回调方法在Intent参数,在“数据”字段。要获取值,调用的getData()。
编辑联系人的联系人ACTION_EDIT CONTENT_LOOKUP_URI。编辑活动将允许用户编辑任何与此联系人相关的数据。 Contacts.CONTENT_ITEM_TYPE,单个联系人。显示在联系人应用程序中编辑联系人屏幕。显示你添加到意图的额外价值。当完成用户点击保存编辑,您的活动返回到前台。
显示一个选择器也可以添加数据。 ACTION_INSERT_OR_EDIT N / A CONTENT_ITEM_TYPE此意图总是显示联系人应用程序的选择器屏幕。用户既可以选择联系人进行编辑,或添加新的联系人。无论是编辑或外接屏幕,这取决于用户的选择,并显示在你的意图传递额外数据。如果您的应用程序显示联系人数据,如电子邮件或电话号码,用这个意图允许用户将数据添加到现有联系人。联系,
注:没有必要送这个意图的演员名字的价值,因为用户总是挑选现有名称或添加一个新的。此外,如果您发送的名称,用户选择做编辑,联系人应用程序将显示您发送的名称,覆盖以前的值。如果用户没有注意到这一点,并保存编辑,旧值会丢失。


该设备的联系人应用程序不允许您删除原始联系人或任何其数据与意图。相反,删除原接触,使用ContentResolver.delete()或ContentProviderOperation.newDelete()。


下面的片段展示了如何构建和发送插入一个新的原始联系人和数据的意图:

// Gets values from the UIString name = mContactNameEditText.getText().toString();String phone = mContactPhoneEditText.getText().toString();String email = mContactEmailEditText.getText().toString();String company = mCompanyName.getText().toString();String jobtitle = mJobTitle.getText().toString();// Creates a new intent for sending to the device's contacts applicationIntent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);// Sets the MIME type to the one expected by the insertion activityinsertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);// Sets the new contact nameinsertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);// Sets the new company and job titleinsertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);/** Demonstrates adding data rows as an array list associated with the DATA key*/// Defines an array list to contain the ContentValues objects for each rowArrayList<ContentValues> contactData = new ArrayList<ContentValues>();/** Defines the raw contact row*/// Sets up the row as a ContentValues objectContentValues rawContactRow = new ContentValues();// Adds the account type and name to the rowrawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());// Adds the row to the arraycontactData.add(rawContactRow);/** Sets up the phone number data row*/// Sets up the row as a ContentValues objectContentValues phoneRow = new ContentValues();// Specifies the MIME type for this data row (all data rows must be marked by their type)phoneRow.put(    ContactsContract.Data.MIMETYPE,    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);// Adds the phone number and its type to the rowphoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);// Adds the row to the arraycontactData.add(phoneRow);/** Sets up the email data row*/// Sets up the row as a ContentValues objectContentValues emailRow = new ContentValues();// Specifies the MIME type for this data row (all data rows must be marked by their type)emailRow.put(    ContactsContract.Data.MIMETYPE,    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);// Adds the email address and its type to the rowemailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);// Adds the row to the arraycontactData.add(emailRow);/** Adds the array to the intent's extras. It must be a parcelable object in order to* travel between processes. The device's contacts app expects its key to be* Intents.Insert.DATA*/insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);// Send out the intent to start the device's contacts app in its add contact activity.startActivity(insertIntent);
数据的完整性


由于联系人存储库包含用户期望是正确的和最新的重要和敏感​​数据,联系人提供商已经明确定义了数据完整性规则。这是你的责任,以符合当你修改联系人数据这些规则。最重要的规则,这里列出:


随时添加一个ContactsContract.CommonDataKinds.StructuredName排,每排ContactsContract.RawContacts添加。
而无需在ContactsContract.Data表中的一行ContactsContract.CommonDataKinds.StructuredName一个ContactsContract.RawContacts行可能会在合并过程中会产生问题。
新ContactsContract.Data行始终链接到他们的父母ContactsContract.RawContacts一行。
未链接到一个ContactsContract.RawContacts排ContactsContract.Data不会在设备的联系人应用程序可见的,则可能会导致同步适配器的问题。
只对那些您所拥有的原始联系人更改数据。
请记住,联系人提供商通常从几个不同的帐户类型/联机服务管理数据。你需要确保你的应用程序只修改或属于您行删除的数据,而且它只有一个帐户类型和名称,你控制插入的数据。
始终使用ContactsContract及其子类中定义的主管部门,内容的URI,URI路径,列名,MIME类型和TYPE值的常量。
使用这些常数可以帮助你避免错误。你如果有任何常量被废弃还可以用编译器警告通知。
自定义数据行


通过创建和使用自己的自定义MIME类型,您可以插入,编辑,删除,并在ContactsContract.Data表中检索自己的数据行。你行仅限于使用ContactsContract.DataColumns定义的列,虽然你可以映射你自己的特定类型的列名的默认列名。在设备的联系人应用程序,将显示您的行数据,但不能编辑或删除,并且用户不能添加额外的数据。要允许用户修改自定义数据行,必须在自己的应用程序提供了一个编辑的活动。


要显示自定义的数据,提供包含<ContactsAccountType>元素和一个或多个<ContactsDataKind>子元素contacts.xml文件。此进行更详细的节<ContactsDataKind>元素说明。


要了解更多有关自定义MIME类型,请阅读创建内容提供商指南。


联系方式供应商同步适配器


在联系人提供专门的设备和在线服务之间的接触处理数据的同步设计。这允许用户对现有数据下载到新的设备和上传现有数据到一个新的帐户。同步还可以确保用户随时掌握最新的数据,而不管添加和更改的来源。同步的另一个优点是,它使触点可用数据即使当设备没有连接到网络。


虽然可以通过多种方式实现同​​步,Android系统提供了一个插件同步框架,可以自动执行以下任务:


检查网络的可用性。
调度和执行同步,根据用户的喜好。
重新启动已停止同步。
要使用此框架,您提供的同步适配器插件。每个同步适配器是唯一的服务和内容提供商,但可以为同一个服务处理多个帐户名。该框架还允许相同的服务和提供多个同步适配器。


同步适配器类和文件


您可以实现同步适配器作为AbstractThreadedSyncAdapter的子类,并安装它作为一个Android应用程序的一部分。该系统得知在应用程序的清单元素同步适配器,从一个特殊的XML文件的清单指向。 XML文件定义了在线服务和内容提供商,它们共同唯一标识适配器的权限帐户类型。直到用户添加了一个帐户同步适配器的帐户类型,使同步的内容提供商同步适配器同步同步适配器并不活跃。在这一点上,在系统启动管理适配器,并称其为必要的内容提供者和服务器之间进行同步。


注意:使用的帐户类型的同步适配器的识别部允许系统检测和组一起同步处理,从相同的组织访问不同服务适配器。例如,谷歌的在线服务同步适配器都具有相同的帐户类型com.google。当用户添加一个谷歌帐户,以自己的设备,所有谷歌服务的安装同步适配器被列在一起;每个同步适配器同步上市与设备上的其他内容提供商。


由于大多数服务都需要用户的访问数据之前验证自己的身份,Android系统提供了一个认证框架,它类似于,并与其同步适配器框架结合经常使用。验证框架使用插件认证程序是AbstractAccountAuthenticator的子类。验证者验证用户的在下面的步骤身份:


收集用户名,密码或类似的信息(用户的凭据)。
发送凭据服务
检查服务的答复。
如果服务接受证书,认证可以存储供以后使用的凭证。因为插件认证器的框架,所述的AccountManager可以以任何authtokens提供接入的认证器支架和选择公开,如OAuth2用户authtokens。


虽然不需要验证,大多数与我们联系服务使用它。不过,你不需要使用Android的身份验证框架做认证。


同步适配器实现


为了实现对联系人提供商同步适配器,您开始通过创建包含以下内容的Andr​​oid应用程序:


响应从系统请求的服务组件绑定到同步适配器。
当系统要运行同步,它调用服务的onBind()方法来得到一个IBinder的同步适配器。这使得该系统能够执行跨进程调用到适配器的方法。
在样本同步适配器示例应用程序,该服务的类名是com.example.android.samplesync.syncadapter.SyncService。


实际的同步适配器,作为AbstractThreadedSyncAdapter的具体子类来实现。
这个类从服务器下载数据,从设备上传数据,和解决冲突的工作。适配器的主要工作是在方法onPerformSync()完成。这个类必须被实例化一个单例。
在样本同步适配器示例应用程序,同步适配器在类com.example.android.samplesync.syncadapter.SyncAdapter定义。


应用的子类。
此类充当一个工厂同步适配器单身。使用onCreate()方法来实例化同步适配器,并提供了一​​个静态的“吸气剂”的方法,以单身返回同步适配器的服务的onBind()方法。
可选:响应从系统用户认证请求的服务组件。
开始的AccountManager这项服务开始验证过程。该服务的onCreate()方法实例化一个认证对象。当系统要为应用程序的同步适配器进行身份验证的用户帐户,它调用服务的onBind()方法来得到一个IBinder的认证。这允许系统做跨进程调用到验证的方法..
在样本同步适配器示例应用程序,该服务的类名是com.example.android.samplesync.authenticator.AuthenticationService。


可选:AbstractAccountAuthenticator的具体子类,后者用于处理身份验证请求。
这个类提供了调用的AccountManager与服务器验证用户的凭据的方法。认证过程的细节千差万别,基于服务器的技术中使用。您应该参考的文档服务器软件,以了解更多有关认证。
在样本同步适配器示例应用程序,验证在类定义com.example.android.samplesync.authenticator.Authenticator。


定义同步适配器和认证者向系统的XML文件。
前面所描述的同步适配器和身份验证服务组件中的<service>在应用程序清单的元素定义。这些元素包含<元数据>子元素提供具体的数据到系统中:
在<元数据>元素的同步适配器服务点XML文件RES / XML / syncadapter.xml。反过来,此文件指定为将与联系人提供同步web服务,并为网络服务账户类型的URI。
可选:为认证指向XML文件RES / XML / authenticator.xml的<元数据>元素。反过来,此文件指定该验证支持在认证过程中出现的帐户类型,以及用户界面的资源。在此元素指定的帐户类型必须相同同步适配器指定的帐户类型。
社会流数据


该android.provider.ContactsContract.StreamItems和android.provider.ContactsContract.StreamItemPhotos表管理从社交网络进入的数据。您可以编写同步适配器,为这些表增加了从自己的网络流数据,也可以从这些表中读取数据流,并在自己的应用程序,或同时显示。有了这些功能,你的社交网络服务和应用程序可以集成到Android的社交网络体验。


社会流中的文本


流的项目总是与原始联系人相关联。该android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID链接为原料接触_ID值。生接触的帐户类型和帐户名称也存储在流项排。


存储从信息流中的数据以下列:


android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
需要。用户的帐户类型与此流项目关联的原始接触。请记住,当你插入一个流项目来设置这个值。
android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
需要。用户的帐户名与此流项目关联的原始接触。请记住,当你插入一个流项目来设置这个值。
标识列
需要。您必须插入当你插入一个流项下列标识列:
android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID:此流项都与相关联的接触的android.provider.BaseColumns#_ID值。
android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY:此流项都与相关联的接触的android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY值。
android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID:此流项都与相关联的原始接触的android.provider.BaseColumns#_ID值。
android.provider.ContactsContract.StreamItemsColumns#评论
可选。你可以在一个流项目的开头显示房屋摘要信息。
android.provider.ContactsContract.StreamItemsColumns#TEXT
流项目的文本,要么被张贴的项目来源,或产生的数据流项目的一些操作的描述的内容。此列可以包含任何格式化且可由fromHtml()被呈现嵌入资源的图像。该供应商可能会截断或ellipsize长的内容,但会尽量避免打破标签。
android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
包含流项目插入或更新,因为在时代的毫秒形式的时间的文本字符串。该插入或更新流项目负责维护此列的应用;它不会自动联系人提供维护。
要显示标识您流的项目信息,请使用android.provider.ContactsContract.StreamItemsColumns#RES_ICON,android.provider.ContactsContract.StreamItemsColumns#RES_LABEL和android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE在您的应用程序链接到资源。


该android.provider.ContactsContract.StreamItems表还包含独家使用的同步适配器通过android.provider.ContactsContract.StreamItemsColumns#SYNC4列android.provider.ContactsContract.StreamItemsColumns#SYNC1。


社会流照片


与流项关联的android.provider.ContactsContract.StreamItemPhotos表中存储的照片。该表的android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID列链接到android.provider.ContactsContract.StreamItems表的_ID列值。相片的引用存储在这些列中的表中:


android.provider.ContactsContract.StreamItemPhotos#PHOTO列(BLOB的)。
照片的二进制表示,由供应商存储和显示调整。此列可用于与联系人提供了用它来存储照片的先前版本的向后兼容性。然而,在目前的版本中,你不应该使用此列来存储照片。相反,使用android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID或android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI(这两者在以下几点中描述)存储照片中的一个文件。这列现在包含照片,这是供阅读的缩略图。
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
照片的原始联系人的数字标识符。这个附加价值,不断DisplayPhoto.CONTENT_URI获得内容URI指向一个图片文件,然后调用openAssetFileDescriptor()来得到一个处理照片文件。
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
A含量URI直接指向由该行表示照片中的照片文件。呼叫openAssetFileDescriptor()与此URI来得到一个处理照片文件。
使用社会流表


这些表的工作一样,在联系人提供其他主要表,不同的是:


这些表需要额外的访问权限。从他们阅读,应用程序必须具有的权限android.Manifest.permission#READ_SOCIAL_STREAM。要修改它们,你的应用程序必须具有的权限android.Manifest.permission#WRITE_SOCIAL_STREAM。
为android.provider.ContactsContract.StreamItems表,存储对每个原始接触的行数是有限的。一旦达到此限制时,联系人提供商通过自动删除具有最老android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP行使得新流项目行的空间。要得到限制,发出查询的内容URI android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI。你可以把所有比URI设置为null,内容等的参数。该查询返回一个包含单个行的光标,跟单柱android.provider.ContactsContract.StreamItems#MAX_ITEMS。
类android.provider.ContactsContract.StreamItems.StreamItemPhotos定义包含单个流项目的照片行android.provider.ContactsContract.StreamItemPhotos的子表。


社会互动流


通过联系人提供商管理,与设备的联系人应用程序相结合的社交数据流,提供了你的社交网络系统中现有的联系人连接的有效方式。以下功能可供选择:


通过同步你的社交网络服务联系人提供者进行同步适配器,您可以检索最近的活动用户的联系人,并将其存储在以后使用android.provider.ContactsContract.StreamItems和android.provider.ContactsContract.StreamItemPhotos表。
除了常规的同步,可以触发同步适配器来获取额外的数据,当用户选择一个联系人进行查看。这允许您同步适配器来获取高分辨率的照片和联系人的最新流项目。
通过注册设备的联系人应用程序和联系人提供的通知,您可以接收意图接触观察时,在这一点从服务更新联系人的状态。这种方法可能更快,使用更少的带宽不是做一个完全同步与同步适配器。
用户可以一边观看在设备的联系人应用程序联系人的联系人添加到您的社交网络服务。你的“邀请联系人”功能,您使用,增加了现有的联系人到您的网络,并提供设备的联系人应用程序的XML文件和应用程序的详细信息联系人提供一个活动的组合使启用此功能。
与联系人提供流项目定期同步是一样的其他同步。要了解更多有关同步,请参见联系人提供商同步适配器。注册通知和邀请联系人盖在接下来的两节。


注册来处理社交网络的意见


要当用户查看的您同步适配器管理联系人注册您的同步适配器接收通知:


在项目中创建的RES / XML /目录下一个名为contacts.xml文件。如果你已经有了这个文件,你可以跳过这一步。
在这个文件中,添加的元素<ContactsAccountType的xmlns:机器人=“htt​​p://schemas.android.com/apk/res/android”>。如果这个元素已经存在,则可以跳过此步骤。
要注册,当用户在设备的联系人应用程序打开一个联系人的详细信息页面被通知的服务,添加属性viewContactNotifyService =“serviceclass”的元素,其中serviceclass是服务的完全限定类名应从收到的意图该设备的联系人应用程序。对于通知服务,使用扩展IntentService类,允许服务接收意向。传入意图的数据包含原始接触用户点击的内容URI。从通知服务,可以绑定到,然后调用您的同步适配器更新数据的原始接触。
要注册一个活动,当用户点击流项目或照片或两者被称为:


在项目中创建的RES / XML /目录下一个名为contacts.xml文件。如果你已经有了这个文件,你可以跳过这一步。
在这个文件中,添加的元素<ContactsAccountType的xmlns:机器人=“htt​​p://schemas.android.com/apk/res/android”>。如果这个元素已经存在,则可以跳过此步骤。
注册您的活动之一来处理用户点击在设备的联系人应用程序流的项目,添加属性viewStreamItemActivity =“activityclass”的元素,其中activityclass是活动的完全限定类名应从收到的意图该设备的联系人应用程序。
注册您的活动之一来处理用户点击在设备的联系人应用程序流的照片,添加属性viewStreamItemPhotoActivity =“activityclass”的元素,其中activityclass是活动的完全限定类名应从收到的意图该设备的联系人应用程序。
在<ContactsAccountType>元件进行更详细的节<ContactsAccountType>元素说明。


传入的​​意图包含用户点击的项目或照片的内容URI。要对文本项独立的活动和照片,在同一文件中使用这两个属性。


与您的社交网络服务进行交互


用户不必离开设备的联系人应用程序来邀请联系人到你的社交网站。相反,你可以有设备的联系人应用程序发送意向邀请接触到你的活动之一。要这样设置:


在项目中创建的RES / XML /目录下一个名为contacts.xml文件。如果你已经有了这个文件,你可以跳过这一步。
在这个文件中,添加的元素<ContactsAccountType的xmlns:机器人=“htt​​p://schemas.android.com/apk/res/android”>。如果这个元素已经存在,则可以跳过此步骤。
添加以下属性:
inviteContactActivity =“activityclass”
inviteContactActionLabel =“@字符串/ invite_action_label”
该activityclass值是应该得到的意图活动的完全限定类名。该invite_action_label值时显示在添加连接菜单中的设备的联系人应用程序中的文本字符串。
注:ContactsSource是ContactsAccountType弃用标记名称。

contacts.xml参考
该文件contacts.xml包含控制您的同步适配器和应用程序与联系人应用程序和联系人提供的互动XML元素。这些元素在下面的章节中描述。
<联系人帐户类型>元素
在<联系人帐户类型>元素控制与联系人应用程序应用程序的交互。它的语法如下

<ContactsAccountType    xmlns:android="http://schemas.android.com/apk/res/android"    inviteContactActivity="activity_name"    inviteContactActionLabel="invite_command_text"    viewContactNotifyService="view_notify_service"    viewGroupActivity="group_view_activity"    viewGroupActionLabel="group_action_text"    viewStreamItemActivity="viewstream_activity_name"    viewStreamItemPhotoActivity="viewphotostream_activity_name">
包含在:


RES / XML / contacts.xml


可以包含:


<ContactsDataKind>


描述:


声明Android组件和UI的标签,允许用户邀请他们的联系人之一的社交网络,当他们的社交网络流之一更新,等等通知用户。


注意,属性前缀机器人:没有必要的<ContactsAccountType>的属性。


属性:


inviteContactActivity
您希望当用户选择添加从设备的联系人应用程序的连接,以激活应用程序中的活动的完全限定类名。
inviteContactActionLabel
这显示在inviteContactActivity指定的活动,在添加连接菜单的文本字符串。例如,你可以使用字符串“按照我的网络”。您可以使用字符串资源标识符此标签。
viewContactNotifyService
在你的应用程序的服务,当用户查看联系人时,应该接收通知的完全限定类名。该通知被设备的联系人应用程序发送;它可以让你的应用程序在需要的,直到他们推迟数据密集型操作。例如,您的应用程序可以通过读取并显示联系人的高分辨率照片和最新的社会流项目该通知作出回应。这个功能进行更详细的部分社会流相互作用说明。你可以看到在SampleSyncAdapter示例应用程序的NotifierService.java文件通知服务的一个例子。
viewGroupActivity
在应用程序中的活动,可以显示组信息的完全限定类名。当用户单击该设备的联系人应用程序组标签,将显示此活动的用户界面。
viewGroupActionLabel
标签的联系人应用程序显示的UI控件,它允许用户看组在应用程序中。
例如,如果你在设备上安装了Google+应用程序,您将Google+同步的联系人应用程序,你会看到如组联系人应用程序的组选项卡中列出的Google+社交圈。如果你点击Google+圈子,你会看到人们在列为“本集团”那个圈子。在显示器的顶部,你会看到一个Google+图标;如果你点击它,控制切换到Google+应用。联系人应用程序做到这一点的viewGroupActivity,使用Google+图标,viewGroupActionLabel的价值。


字符串资源标识符允许对这种属性。


viewStreamItemActivity
在应用程序中的活动的完全限定类名,当用户点击一个联系人原料流项目设备的联系人应用程序启动。
viewStreamItemPhotoActivity
在应用程序中的活动的完全限定类名,当用户单击原始联系人的流项目照片的设备的联系人应用程序启动。
<ContactsDataKind>元素


在<ContactsDataKind>元素控制应用程序的自定义数据行的联系人应用程序的用户界面的显示屏。它的语法如下:

<ContactsDataKind    android:mimeType="MIMEtype"    android:icon="icon_resources"    android:summaryColumn="column_name"    android:detailColumn="column_name">
包含在:


<ContactsAccountType>
描述:


使用这个元素有联系人应用程序显示自定义的数据行的内容作为原始联系人的详细信息部分。每个<ContactsDataKind>子元素<ContactsAccountType>代表一个类型的自定义数据行的,你的同步适配器添加到ContactsContract.Data表。添加一个<ContactsDataKind>元素为您使用每个自定义MIME类型。您不必如果您有您不希望显示数据的自定义数据行要添加的元素。


属性:


机器人:mime类型
自定义MIME类型你在ContactsContract.Data表自定义数据行类型定义。例如,值vnd.android.cursor.item / vnd.example.locationstatus可以是自定义的MIME类型,记录联系人的最后已知​​位置的数据行。
安卓图标
一个Android绘制资源,该联系人应用程序旁边会显示您的数据。这是用来表示该数据来自您的服务的用户。
机器人:summaryColumn
列名第一个从数据行检索两个值。值显示作为该数据行的条目的第一行。第一行旨在被用作数据的总结,但是这是可选的。又见机器人:detailColumn。
机器人:detailColumn
列名从数据行检索两个值的第二个。值显示作为该数据行的条目的第二行。又见机器人:summaryColumn。
其他联系方式供应商特点


除了在前面的章节中描述的主要特点,联系人提供商提供这些有用的功能与联系人数据的工作:


联系人组
拍照功能
联系人组


联系人提供者可选择与组数据相关的联系人的标签集合。如果与用户帐户相关联的服务器要维护组,该帐户的帐户类型的同步适配器应联系人提供商和服务器之间传输组的数据。当用户添加新的联系人到服务器,然后把新组该联系人,同步适配器必须添加新组到ContactsContract.Groups表。原料接触所属的一个或多个组,以被存储在ContactsContract.Data表,使用ContactsContract.CommonDataKinds.GroupMembership MIME类型。


如果你正在设计一个同步适配器将从服务器到联系人提供程序添加原始联系人数据,而没有使用组,那么你需要告诉供应商,以使您的数据可见。在这时候,用户添加的帐户的装置执行的代码,更新ContactsContract.Settings一行联系人提供的帐户补充道。在此行中,Settings.UNGROUPED_VISIBLE列的值设置为1。当你这样做时,联系人提供商将永远让你的通讯录中的数据可见,即使你不使用群体。


联系人照片


该ContactsContract.Data表存储的照片作为MIME类型Photo.CONTENT_ITEM_TYPE行。行的CONTACT_ID列链接到原始接触到它所属的_ID列。该类ContactsContract.Contacts.Photo定义包含联系人的主照片,这是该联系人的主要原料的主要联系人照片照片信息ContactsContract.Contacts的子表。同样地,类ContactsContract.RawContacts.DisplayPhoto定义包含用于生接触的主照片照片信息ContactsContract.RawContacts的子表。


对于ContactsContract.Contacts.Photo和ContactsContract.RawContacts.DisplayPhoto的参考文档包含检索照片信息的例子。没有为返回主缩略图原始接触没有方便的类,但你可以发送一个查询到ContactsContract.Data表,选择对原始联系人的_ID的Photo.CONTENT_ITEM_TYPE和IS_PRIMARY栏里找到原始联系人的首选照片一行。


社交数据流,一个人可能还包括照片。这些被存储在android.provider.ContactsContract.StreamItemPhotos表,这是在更详细的部分社会流照片说明。


更多相关文章

  1. “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
  2. Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
  3. 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
  4. GPS启动分析
  5. ListActivity使用注意
  6. ContentProvider实现数据共享1
  7. http上传文件到网络核心代码
  8. Android(安卓)ListView实现快速定位联系人功能【转发】
  9. Android(安卓)GUI Building Blocks

随机推荐

  1. Python 爬虫进阶必备 | 某k12注册加密参
  2. 理财实战之基金定投(三)
  3. Python 爬虫进阶必备 | 某爬虫练习站之 j
  4. 理财实战之基金定投(二)
  5. Prometheus 之 Grafana介绍及安装
  6. 理财实战之基金定投(一)
  7. linux_学习之忘记密码处理办法
  8. 今日网站
  9. 理财扫盲之什么叫通货膨胀
  10. RPM打包,安装到指定目录