Android Contact provider基础

Contact provider

Contact provider是一个强大而又灵活的 Android 组件,用于管理设备上有关联系人数据的中央存储库。 Contact provider是您在设备的联系人应用中看到的数据源,您也可以在自己的应用中访问其数据,并可在设备与在线服务之间传送数据。 提供程序储存有多种数据源,由于它会试图为每个联系人管理尽可能多的数据,因此造成其组织结构非常复杂。 为此,该提供程序的 API 包含丰富的协定类和接口,为数据检索和修改提供便利。

本指南介绍下列内容:

  • 提供程序基本结构
  • 如何从提供程序检索数据
  • 如何修改提供程序中的数据
  • 如何编写用于同步服务器数据与联系人提供程序数据的同步适配器。

Contact provider(联系人提供程序组织)


联系人提供程序是 Android 内容提供程序的一个组件。它保留了三种类型的联系人数据,每一种数据都对应提供程序提供的一个表,如图 1 所示:
这里写图片描述
图 1. 联系人提供程序表结构。

这三个表通常以其协定类的名称命名。这些类定义表所使用的内容 URI、列名称及列值相应的常量:

ContactsContract中的协定类表示的其他表是辅助表,Contact provider利用它们来管理其操作,或为设备的联系人或电话应用中的特定功能提供支持。

Original Contacts(原始联系人)


一个原始联系人表示来自某一帐户类型和帐户名称、有关某个联系人的数据。 由于Contact provider允许将多个在线服务作为某一联系人的数据源,因此它允许同一联系人对应多个原始联系人。 借助支持多个原始联系人的特性,用户还可以将某一联系人在帐户类型相同的多个帐户中的数据进行合并。

原始联系人的大部分数据并不存储在 ContactsContract.RawContacts 表内,而是存储在 ContactsContract.Data 表中的一行或多行内。每个数据行都有一个 Data.RAW_CONTACT_ID 列,其中包含其父级 ContactsContract.RawContacts 行的 android.provider.BaseColumns#_ID RawContacts._ID 值。

重要的原始联系人列
表 1 列出了 ContactsContract.RawContacts 表中的重要列。 请阅读表后的说明:

表 1. 重要的原始联系人列。

列名称 用途 备注
ACCOUNT_NAME 作为该原始联系人来源的帐户类型的帐户名称。 例如,Google 帐户的帐户名称是设备所有者的某个 Gmail 地址 此名称的格式专用于其帐户类型。它不一定是电子邮件地址。
ACCOUNT_TYPE 作为该原始联系人来源的帐户类型。例如,Google 帐户的帐户类型是 com.google。 请务必使用您拥有或控制的域的域标识符限定您的帐户类型。 这可以确保您的帐户类型具有唯一性。 提供联系人数据的帐户类型通常关联有同步适配器,用于与Contact provider进行同步。
DELETED 提供联系人数据的帐户类型通常关联有同步适配器,用于与Contact provider进行同步。 此标志让Contact provider能够在内部保留该行,直至同步适配器能够从服务器删除该行,然后再从存储库中最终删除该行。

说明
以下是关于 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),然后您的应用才能添加原始联系人行。 您可以在文档中向用户解释这项要求,也可以提示用户添加类型和名称,或者同时采用这两种措施。

Original contact data sources(原始联系人数据来源)

为理解原始联系人的工作方式,假设有一位用户“Emily Dickinson”,她的设备上定义了以下三个用户帐户:

  • emily.dickinson@gmail.com
  • emilyd@gmail.com
  • Twitter 帐户“belle_of_amherst”

该用户已在 Accounts 设置中为全部三个帐户启用了 Sync Contacts。

假定 Emily Dickinson 打开一个浏览器窗口,以 emily.dickinson@gmail.com 身份登录 Gmail,然后打开 “联系人”,并添加“Thomas Higginson”。后来,她以 emilyd@gmail.com 身份登录 Gmail,并向“Thomas Higginson”发送一封电子邮件,此操作会自动将他添加为联系人。 她还在 Twitter 上关注了“colonel_tom”(Thomas Higginson 的 Twitter ID)。

以上操作的结果是,联系人提供程序会创建以下这三个原始联系人:

  1. 第一个原始联系人对应“Thomas Higginson”,关联帐户 emily.dickinson@gmail.com。 用户帐户类型是 Google。
  2. 第二个原始联系人对应“Thomas Higginson”,关联帐户 emilyd@gmail.com。 用户帐户类型也是 Google。由于添加的联系人对应的用户帐户不同,因此尽管名称与前一名称完全相同,也只能作为第二个原始联系人。
  3. 第三个原始联系人对应“Thomas Higginson”,关联帐户“belle_of_amherst”。用户帐户类型是 Twitter。

Data(数据)


如前文所做的说明,原始联系人的数据存储在一个 ContactsContract.Data 行中,该行链接到原始联系人的 _ID 值。这使一位原始联系人可以拥有多个具有相同数据类型的实例,例如电子邮件地址或电话号码。 例如,如果对应 emilyd@gmail.com 的“Thomas Higginson”(关联 Google 帐户 emilyd@gmail.com 的 Thomas Higginson 的原始联系人行)的住宅电子邮件地址为 thigg@gmail.com,办公电子邮件地址为 thomas.higginson@gmail.com,则联系人提供程序会存储这两个电子邮件地址行,并将它们都链接到原始联系人。

请注意,这个表中存储了不同类型的数据。显示姓名、电话号码、电子邮件、邮政地址、照片以及网站明细行都可以在 ContactsContract.Data 表中找到。 为便于管理这些数据, ContactsContract.Data 表为一些列使用了描述性名称,为其他列使用了通用名称。 使用描述性名称的列的内容具有相同的含义,与行中数据的类型无关,而使用通用名称的列的内容则会随数据类型的不同而具有不同的含义。

Descriptive Column Name(描述性列名称)

以下是一些描述性列名称的示例:

  • RAW_CONTACT_ID
    该数据对应的原始联系人 _ID 列的值。
  • MIMETYPE
    该行中存储的数据类型,以自定义 MIME(多用途互联网邮件扩展)类型表示。联系人提供程序使用了 ContactsContract.CommonDataKinds 子类中定义的 MIME 类型。 这些 MIME 类型为开源类型,可供与联系人提供程序协作的任何应用或同步适配器使用。
  • IS_PRIMARY
    如果一个原始联系人可能具有多个这种类型的数据行, IS_PRIMARY 列会标记 包含该类型主要数据的数据行。例如,如果用户长按某个联系人的电话号码,并选择 Set default,则包含该号码的 ContactsContract.Data 行会将其 IS_PRIMARY 列设置为一个非零值。

General Column Name(通用列名称)

有 15 个通用列命名为 DATA1 至 DATA15,可普遍适用;还有四个通用列命名为 SYNC1 至 SYNC4,只应由同步适配器使用。 通用列名称常量始终有效,与行包含的数据类型无关。

DATA1 列为索引列。联系人提供程序总是在此列中存储其预期会成为最频繁查询目标的数据。 例如,在一个电子邮件行中,此列包含实际电子邮件地址。

按照惯例,DATA15 为预留列,用于存储照片缩略图等二进制大型对象 (BLOB) 数据。

Type-specific column names(类型专用列名称)

为便于处理特定类型行的列,Contact provider还提供了 ContactsContract.CommonDataKinds 子类中定义的类型专用列名称常量。 这些常量只是为同一列名称提供不同的常量名称,这有助于您访问特定类型行中的数据。

例如,ContactsContract.CommonDataKinds.Email 类为 ContactsContract.Data 行定义类型专用列名称常量,该行的 MIME 类型为 Email.CONTENT_ITEM_TYPE。 该类包含电子邮件地址列的 ADDRESS 常量。 ADDRESS 的实际值为“data1”,这与列的通用名称相同。

注意:请勿使用具有提供程序某个预定义 MIME 类型的行向 ContactsContract.Data 表中添加您自己的自定义数据。 否则您可能会丢失数据,或导致提供程序发生故障。 例如,如果某一行具有 MIME 类型 Email.CONTENT_ITEM_TYPE,并且 DATA1 列包含的是用户名而不是电子邮件地址,您就不应添加该行。如果您为该行使用自定义的 MIME 类型,则可自由定义您的自定义类型专用的列名称,并随心所欲地使用这些列。

图 2 显示的是描述性列和数据列在 ContactsContract.Data 行中的显示情况,以及类型专用列名称“覆盖”通用列名称的情况
这里写图片描述
图 2. 类型专用列名称和通用列名称。

Type the name of the class dedicated column(类型专用列名称类)

表 2 列出了最常用的类型专用列名称类:

表 2. 类型专用列名称类

映射类 数据类型 备注
ContactsContract.CommonDataKinds.StructuredName 与该数据行关联的原始联系人的姓名数据。 一位原始联系人只有其中一行。
ContactsContract.CommonDataKinds.Photo 与该数据行关联的原始联系人的主要照片。 一位原始联系人只有其中一行。
ContactsContract.CommonDataKinds.Email 与该数据行关联的原始联系人的电子邮件地址。 一位原始联系人可有多个电子邮件地址。
ContactsContract.CommonDataKinds.StructuredPostal 与该数据行关联的原始联系人的邮政地址。 一位原始联系人可有多个邮政地址。
ContactsContract.CommonDataKinds.GroupMembership 将原始联系人链接到Contact provider内其中一组的标识符。 组是帐户类型和帐户名称的一项可选功能。

Contacts(联系人)

Contact provider通过将所有帐户类型和帐户名称的原始联系人行合并来形成联系人。 这可以为显示和修改用户针对某一联系人收集的所有数据提供便利。 联系人提供程序管理新联系人行的创建,以及原始联系人与现有联系人行的合并。 系统不允许应用或同步适配器添加联系人,并且联系人行中的某些列是只读列。

注:如果您试图通过 insert() 向联系人提供程序添加联系人,会引发一个 UnsupportedOperationException 异常。 如果您试图更新一个列为“只读”的列,更新会被忽略。

如果添加的新原始联系人不匹配任何现有联系人,Contact provider会相应地创建新联系人。 如果某个现有原始联系人的数据发生了变化,不再匹配其之前关联的联系人,则提供程序也会执行此操作。 如果应用或同步适配器创建的新原始联系人“的确”匹配某位现有联系人,则新原始联系人将与现有联系人合并。

Contact provider通过 Contacts 表中联系人行的 _ID 列将联系人行与其各原始联系人行链接起来。 原始联系人表 ContactsContract.RawContacts 的 CONTACT_ID 列包含对应于每个原始联系人行所关联联系人行的 _ID 值。

ContactsContract.Contacts 表还有一个 android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY 列,它是一个指向联系人行的“永久性”链接。 由于联系人提供程序会自动维护联系人,因此可能会在合并或同步时相应地更改联系人行的 android.provider.BaseColumns#_ID 值。 即使发生这种情况,合并了联系人 android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY 的内容 URI CONTENT_LOOKUP_URI 仍将指向联系人行,这样,您就能使用 android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY 保持指向“最喜爱”联系人的链接,以及执行其他操作。 该列具有其自己的格式,与 android.provider.BaseColumns#_ID 列的格式无关。

图 3 显示的是这三个主要表的相互关系。
这里写图片描述
图 3. 联系人表、原始联系人表与详细信息表之间的关系。

Data from the synchronization adapter(来自同步适配器的数据)


虽然用户是直接将联系人数据输入到设备中,但这些数据也会通过同步适配器从 Web 服务流入联系人提供程序中,这些同步适配器可自动化设备与服务之间的数据传送。 同步适配器在系统控制下在后台运行,它们会调用 ContentResolver 方法来管理数据。

在 Android 中,与同步适配器协作的 Web 服务通过帐户类型加以标识。 每个同步适配器都与一个帐户类型协作,但它可以支持该类型的多个帐户名称。 原始联系人数据来源部分对帐户类型和帐户名称做了简要描述。 下列定义提供了更多详细信息,并描述了帐户类型及帐户名称与同步适配器及服务之间的关系。

  • 帐户类型
    表示用户在其中存储数据的服务。在大多数时候,用户需要向服务验证身份。 例如,Google Contacts 是一个以代码 google.com 标识的帐户类型。 该值对应于 AccountManager 使用的帐户类型。

  • 帐户名称
    **表示某个帐户类型的特定帐户或登录名。**Google Contacts 帐户与 Google 帐户相同,都是以电子邮件地址作为帐户名称。 其他服务可能使用一个单词的用户名或数字 ID。

帐户类型不必具有唯一性。用户可以配置多个 Google Contacts 帐户并将它们的数据下载到联系人提供程序;如果用户为个人帐户名称和工作帐户名称分别设置了一组联系人,就可能发生这种情况。 帐户名称通常具有唯一性。 它们共同标识联系人提供程序与外部服务之间的特定数据流。

如果您想将服务的数据传送到联系人提供程序,则需编写您自己的同步适配器。

图 4 显示的是联系人提供程序如何融入联系人数据的流动。 在名为“同步适配器”的方框中,每个适配器都以其帐户类型命名。
这里写图片描述
图 4. 联系人提供程序数据流。

The Required Permissions(所需权限)


想要访问Contact provider的应用必须请求以下权限:

  • 对一个或多个表的读取权限
    READ_CONTACTS,在 AndroidManifest.xml 中指定,使用 <uses-permission> 元素作为 <uses-permission android:name="android.permission.READ_CONTACTS">
  • 对一个或多个表的写入权限
    WRITE_CONTACTS,在 AndroidManifest.xml 中指定,使用 <uses-permission> 元素作为 <uses-permission android:name="android.permission.WRITE_CONTACTS">
    这些权限不适用于用户个人资料数据。

请切记,用户的联系人数据属于个人敏感数据。用户关心其隐私权,因此不希望应用收集有关其自身的数据或其联系人的数据。 如需权限来访问其联系人数据的理由并不充分,用户可能给您的应用作出差评或干脆拒绝安装。

User Profile(用户个人资料)


ContactsContract.Contacts 表有一行包含设备用户的个人资料数据。 这些数据描述设备的 user 而不是用户的其中一位联系人。 对于每个使用个人资料的系统,该个人资料联系人行都链接到某个原始联系人行。 每个个人资料原始联系人行可具有多个数据行。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 profile
mProjection = new String[]
    {
        Profile._ID,
        Profile.DISPLAY_NAME_PRIMA
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值