最后一篇API译文,的资料进一步整合到新

2019-06-02 08:18 来源:未知

谷歌(Google) 近来发表 谷歌(Google) 正式差异为四个分小名称为 Photos 和 Streams 的服务,并跟 Hangouts 一齐成为公司旗下八个「首要的领域」,而昨天 谷歌则发布正式推出新版 Contacts。这么些新版服务会将 Gmail 和 谷歌(Google) 的新闻整合到相关的关联人资料个中,让两边音信的涉嫌变得更加大。它也明白提出用户把看似一样的维系人音讯统壹,减低资料重新的时机。另三个至关心重视要正是能够提供一个列表,把用户列为喜爱联系人和接触得最频密的联络员11顺序排列。在点进联系人的卡牌内之时,除了可以见到联络对方的秘技之外,还恐怕会把用户跟对方过往在 谷歌 生态系统内的触发纪录体现出来。这一个新版 Contacts 将会在以后几星期内整合到 Gmail 个中,但想事先预览一下的话就点引用来源呢。

Contacts Provider

二零一九年加盟了某字幕组,加之杂事颇多,许久未添新文了,惭愧之极。 在据书上说谷歌(Google) 就要再次回到中华人民共和国后,近期忽又发掘官方网站正在释放 API 汉语版,举个例子本文。当然不是豪门所译,但起码句子结构较通顺,窃以为比 MSDN 中文版好些。虽有一点点工巧(比如将 Provider 译为“提供者”,风趣得紧),但幸而前所未有,只怕 Google自此便统一了温馨的华语术语也未可见。能让越来越多的国人准确驾驭 Android 的精髓,分明是好事,希望 谷歌 继续百折不挠。
那事应该喜大普奔啊,怎么没见报纸发表啊?当年进入的翻译组合了余党,未来余党也已未有了,虽伊始便知是早晚的事,仍不免生出过多凄婉。本身未来将不再出这么些代理译文了

英文原著:
征集日期:201肆-5-1八
法定译文:

概览

  • Android 提供的联系人音讯数据库。
  • 与 Web 同步。
  • 与社调换数据整合。

在本文中

  1. Contacts Provider 组织架构
  2. Raw Contact 表
  3. Data 表
  4. Contacts 表
  5. 来自 Sync Adapter 的数据
  6. 所需权限
  7. 用户个人资料
  8. Contacts Provider 元数据定义
  9. 访问 Contacts Provider
    1. Contacts Provider Sync Adapter
  10. 社交换数据
  11. Contacts Provider 的任何职能

关键类

  1. ContactsContract.Contacts
  2. ContactsContract.RawContacts
  3. ContactsContract.Data
  4. ContactsContract.StreamItems

相关示例

  1. Contact Manager
  2. Sync Adapter

参阅

  1. Content Provider 基础

Contacts Provider 是一种强大而灵活的 Android 组件,它管理着日前设备中的联系人音信数据库。 Contacts Provider 是“联系人”应用的数据源,别的应用也能够访问在那之中的多少,并且能够在当前设施和在线服务中间传输数据。 Contacts Provider 可容纳三种数据源,并且为了尽量将职员音讯全体管制起来,它的团体架构是相比较复杂的。 因而,Contacts Provider 的 API 包涵了多量的契约(contract)类和接口,为读取和修改数据提供便宜。

本文首要介绍了:

  • Provider 基本架构。
  • 怎样从 Provider 读取数据。
  • 怎么着修改 Provider 中的数据。
  • 怎样编写三个 Sync Adapter,完毕服务器数据与 Contacts Provider 的一路。

本文假定读者已对 Android 的 Content Provider 有了开班的垂询。 有关 Content Provider 的愈来愈多新闻,请参阅开辟指南开中学的 Content Provider 基础。 Sync Adapter 范例 中提交了八个用到实例,利用 Sync Adapter 在 Contacts Provider 和托管于 谷歌 Web Service 内的二个演示应用之间开始展览多少传输。

Contacts Provider 协会架构

Contacts Provider 是2个Android Content Provider 组件。 它珍惜着三种职员消息有关的数据,每一种数据对应着 Provider 中的3个表,如图 一 所示:

永利集团304手机版 1

图 1. Contacts Provider 表结构。

那八个表通常通过它们的契约类名称来引用。 那些类为下列数据表的 Content U翼虎I、字段名和字段值举办了相应常量的定义:

ContactsContract.Contacts
每行数据意味着3个牵连人,这里已对 Raw Contact 数据记录做过归并。

ContactsContract.RawContacts
存放联系人的摘要音信,依据“账户名称 账户类型”来唯1鲜明人士。

ContactsContract.Data
存放 Raw Contact 的详细音讯,比方 Email 地址、电话号码等。

ContactsContract 中还会有别的一些援救表,也是由契约类来代表的,都是Contacts Provider 用来进展操作管理的,或是用于向“联系人”或“电话”应用提供特定作用的。

RawContacts 表

3个 Raw Contact 代表由账户类型和账户名唯1明确的人手音信。 因为 Contacts Provider 允许同一联系人使用三个在线服务作为数据源,所以它同意一位对应八个 Raw Contact。 正因那样,2个用户的多寡可由一样账户类型的多少个账户归并转移。

Raw Contact 的半数以上数目并不是保留在 ContactsContract.RawContacts 表中。而是存放于 ContactsContract.Data 表的一条或多条记下中。每行数据都有一个 Data.RAW_CONTACT_ID 字段,存放着上一流 ContactsContract.RawContacts 的记录 RawContacts._ID

RawContacts 的关键字段

表 一 中列出了ContactsContract.RawContacts 中的首要字段。请留意表中的注意事项。

表 1 RawContacts 的重要字段:

字段名称 用途 注意事项
ACCOUNT_NAME 该 Raw Contact 数据来源的账户名称,与账户类型呼应。 比如,Google 账户的名称是个 Gmail 地址。详情请参阅下面的 ACCOUNT_TYPE 账户名称的格式根据账户类型而定。不一定是 Email 地址。
ACCOUNT_TYPE 该 Raw Contact 数据来源的账户类型。比如,Google 账户的类型是 com.google。 请务必使用拥有所有权或控制权的域名作为账户类型的修饰符,以保证账户类型的唯一性。 联系人的账户类型通常与某个 Sync Adapter 关联起来,以便与 Contacts Provider 同步数据。
DELETED 该 Raw Contact 的“删除”标志。 本标志允许 Contacts Provider 在内部暂时保留这条记录,等到 Sync Adapter 完成服务器上的删除操作后,再从数据库中最终删除这条记录。

注意

以下是有关 ContactsContract.RawContacts 表的重要事项:

  • Raw Contact 的人名部分从没存放在 ContactsContract.RawContacts 的笔录中。而是保存在 ContactsContract.Data 表中,类型为 ContactsContract.CommonDataKinds.StructuredName 。每个 Raw Contact 在 ContactsContract.Data 表中只设有一条该项指标数目。
  • 注意:一旦要在 Raw Contact 记录中接纳本人的账户新闻,必须先用 AccountManager 实行挂号。只要让用户把账户类型和账户名称出席账户列表就可以。 如若未经注册,Contacts Provider 将会活动删除自行参与的 Raw Contact 数据行。

    譬喻说,假使应用程序须要对一个基于 Web 服务的关联人音信实行管制, 服务的域名是 com.example.dataservice, 服务的账户类型是 becky.sharp@dataservice.example.com, 那么在增加 Raw Contact 记录以前, 首先必须让用户拉长账户“类型”(com.example.dataservice)和账户“名称”(becky.smart@dataservice.example.com)。 能够在应用程序的文书档案中向用户表达那一必要,也能够在先后中晋升用户增加类型和称号。 关于账户类型和账户名称的越来越多细节,就要下一节中牵线。

Raw Contact 的数量来自

为了强化对 Raw Contact 的知情,以下举个例子表明,假定用户“艾米丽Dickinson”在设施中持有以下三个账户:

  • emily.dickinson@gmail.com
  • emilyd@gmail.com
  • Twitter 账号 "belle_of_amherst"

该用户在系统安装项 账户 中对那么些账号都启用了 联合联系人功能。

要是 艾米丽 狄克inson 张开浏览器窗口,用 emily.dickinson@gmail.com 登入Gmail,并张开联系人,增多“托马斯 Higginson”。 然后,她又用 emilyd@gmail.com 登陆 Gmail 并发送邮件给“托马斯Higginson”,那会活动将其增加为联系人。 同不平日间,她还在 推特上关怀了“colonel_tom”(Thomas Higginson 的 Twitter ID)。

成就上述操作后,Contacts Provider 将会调换以下叁条 Raw Contact 记录:

  1. 率先条 Raw Contact 记录对应“托马斯 Higginson”,关联账户是 emily.dickinson@gmail.com。 账户类型为 谷歌。
  2. 第二条 Raw Contact 记录对应“托马斯 Higginson”,关联账户是 emilyd@gmail.com。 账户类型也是 谷歌(Google)。由于对应的用户帐户分歧,固然第三条记下的名称与前一条的完全同样,如故成为了首个Raw Contact。
  3. 其3条 Raw Contact 记录对应“托马斯 Higginson” ,关联账户是“belle_of_amherst”。 账户类型是 Twitter。

Data 表

如前所述,Raw Contact 的数目存放在 ContactsContract.Data 中,并通过 Raw Contact 记录的 _ID 关联。 那样种种 Raw Contact 对每个数据(如 Email 地址或电话号码)都足以具备五个实例。 比如,“Thomas Higginson”的 Raw Contact 记录为 emilyd@gmail.com(通过 Google 账户 emilyd@gmail.com 关联), 她的生活费 Email 地址是 thigg@gmail.com,工作 Email 地址是 thomas.higginson@gmail.com, Contacts Provider 就能蕴藏两条 Email 记录,且均通过 ID 与 Raw Contact 记录关联。

请小心,Data 表中存放着多样类型的数码。展现姓名、电话号码、Email、邮寄地址、照片和网址实际情况等音讯都能够在 ContactsContract.Data 表中找到。为了便于管理, ContactsContract.Data 表的多少字段名称是描述性的,另壹部分则是通用名称。 描述性名称的字段与数据的项目非亲非故,内容的意义与字段名称同样。 通用名称字段中的内容,则会基于分歧的数据类型而具有分歧的意思。

描述性的字段名称

以下是局地描述性字段名称的事例:

RAW_CONTACT_ID
对应 Raw Contact 的 _ID

MIMETYPE
行当数据的项目,以某种 MIME 类型的款式提交。 Contacts Provider 将会接纳 ContactsContract.CommonDataKinds 中定义的 MIME 类型。这几个 MIME 类型都是当众定义的,能够被应用程序或 Contacts Provider 对应的 Sync Adapter 间接动用。

IS_PRIMARY
倘诺 Raw Contact 的某1类联系音讯大概对应多条数据行,IS_PRIMARY 字段就标志了哪壹行数据是此类音讯的主数据。 例如,假若用户长按某联系人的电话号码,并精选 设为默许号码,那么带有该号码的 ContactsContract.Data 数据行的 IS_PRIMARY 字段就能被置为非零值。

通用字段名称

当下可用的通用字段有15个,名字分别为 DATA1DATA15。 其它还只怕有几个通用字段是 Sync 艾达pter 专项使用的,命名称叫 SYNC1SYNC4。 通用字段名称随时可用,与数据行的档期的顺序无关。

DATA1 字段自带索引。 Contacts Provider 始终会利用这一字段,将其身为查询语句中最常用的数据所在。 举个例子,在 Email 数据行中,本字段就存放了实在的 Email 地址。

奉公守法规矩,DATA15 是保留字段,用于存放二进制大对象(BLOB)数据,举个例子缩略图。

一定类型的字段名称

为了简化对有个别特定项目数据的操作,Contacts Provider 也援助部分稳固类型的字段名称,这一个常量在 ContactsContract.CommonDataKinds 的子类中定义。通过这一个常量,就能够一本万利地走访同名的数码字段。

例如, ContactsContract.CommonDataKinds.Email 类中就为 MIME 类型为 Email.CONTENT_ITEM_TYPEContactsContract.Data 数据行定义了有的字段。该类中含有了 Email 地址字段 ADDRESSADDRESS 的值实际就是通用字段名称“data一”。

注意: 不要套用 Provider 预订义的 MIME 类型在 ContactsContract.Data 表中增加自定义的多寡。那可能会促成数据丢失或 Provider 的功效十一分。 比方,请勿在 DATA1 字段中增多 MIME 类型为 Email.CONTENT_ITEM_TYPE 的用户名数据,这里本应是存放在 Email 地址的。 如若该行利用的是自定义的 MIME 类型,那就足以定义跋扈等级次序的字段名称,并随意使用各种字段。

图 二 示范了描述性字段和 data 字段在 ContactsContract.Data 表中的地点,以及定位类型的字段名“覆盖”通用字段名的意况。

永利集团304手机版 2

图 2 固定类型字段与通用字段名称

概念了定点类型字段名称的类

表 二 列出了诸多常用的原则性类型字段名称类:

Table 2. 固定类型字段名称类

映射类 数据类型 注意事项
ContactsContract.CommonDataKinds.StructuredName 本条记录相关 Raw Contact 的姓名。 每个联系人只能有一条该记录。
ContactsContract.CommonDataKinds.Photo 本条记录相关 Raw Contact 的主照片。 每个联系人只能有一条该记录。
ContactsContract.CommonDataKinds.Email 本条记录相关 Raw Contact 的 Email 地址。 每个联系人可以有多个 Email 地址。
ContactsContract.CommonDataKinds.StructuredPostal 本条记录相关 Raw Contact 的邮寄地址。 每个联系人可以有多个邮寄地址。
ContactsContract.CommonDataKinds.GroupMembership 联系人在 Contacts Provider 中所属的组。 群组是账户类型与名称之外的可选功能。详情请参阅 联系人群组

Contacts 表

据说账户类型和账户名称,Contacts Provider 将多条 Raw Contact 记录归并,成为八个联系人。 当需求修改某联系人相关的装有消息时,那会比较便利。 Contacts Provider 负担制造新的 Contacts 记录,并会把多条 Raw Contact 记录与已有的 Contacts 记录关联。 应用程序和 Sync Adapter 都无权增加 Contacts 记录,而且内部的一点字段依旧只读的。

注意: 当试图用 insert() 在 Contacts Provider 中增多关系人记录时,会触发 UnsupportedOperationException 极度。而对“只读”字段的改换将被会忽略。

当某条新 Raw Contact 记录与已部分 Contacts 记录均无法协作时, Contacts Provider 会创立一条新的 Contacts 记录。 在 Raw Contact 记录被修改今后,假设不再匹配在此以前的 Contacts 记录了,Contacts Provider 也会创立一条新的 Contacts 记录。 假若应用程序或 Sync Adapter 新建的 Raw Contact 记录确实与已有的 Contacts 记录相相称,则会创制关系关系。

Contacts Provider 利用 Contacts 表的 _ID 字段将 Contacts 记录与 Raw Contact 记录关联起来。 ContactsContract.RawContacts 表的 CONTACT_ID 字段中存放了有关 Contacts 记录的 _ID 值。

Contacts 表中还带有着1个字段 LOOKUP_KEY ,那是每条记下的“固定”标志。 因为 Contacts 表是由 Contacts Provider 自动保养的,在归并或壹块联系人数量时, _ID 的值也许会被其修改。然则带有 LOOKUP_KEY 的 URI CONTENT_LOOKUP_URI 照旧会针对原有的 Contact 记录,由此得以用 LOOKUP_KEY 对维系人完结“收藏”等操作。该字段有谈得来的格式,与 _ID 字段的格式非亲非故。

图 3 给出了叁张表之间的关系。

永利集团304手机版 3

图 3. Contacts、Raw Contacts 和 Details 表的关系。

来自 Sync Adapter 的数据

Contacts Provider 中的联系人音讯方但是由用户录入的,也足以通过 Sync Adapter 从 Web 服务端插入, 这种服务端和装置之间的多少传输是全自动达成的。 Sync Adapter 运维于后台,由系统通过 ContentResolver 举行田间管理。

在 Android 平高雄,与 Sync Adapter 合作的 Web 服务端是由账户类型标志的。 每一个 Sync Adapter 与壹种账户类型涉及,但足以支撑同1种等级次序下的七个账户名称。 账户类型和账户名称已在 Raw Contact 的数额出自 1节中张开过简介了。上边将介绍更加的多细节,表明账户类型和账户名称是怎么与 Sync Adapter 及后台服务相关联的。

账户类型
标记了壹种可供用户存款和储蓄数据的劳务。大大多景观下,用户必须通过证实技巧利用那个服务。 比如,谷歌(Google) Contacts 正是1种账户类型,并被标识为 google.comAccountManager 将使用这一个值来辨别相应的账户类型。

账户名称
标记了某种账户类型的一个账户或登6名。谷歌 Contacts 账户正是 谷歌(Google)账户,它是用三个 Email 地址作为账户名称的。 别的服务只怕会用2个单词或数字 ID 作为用户名。

账户类型不自然是绝无仅有的。用户能够配备三个 谷歌 Contacts 账户,并把个别的数目下载到 Contacts Provider 中。 这恐怕是出于他有壹部分账户是自身人使用的,而另一些账户则用来工作。 账户名称平常是唯壹的。它和账户类型合在一齐,唯一标志了 Contacts Provider 和外部服务中间的一条数据链。

为了能将自有劳动的多少传递给 Contacts Provider,必要编写制定本人的 Sync Adapter。 越多细节就要 Contacts Provider Sync Adapter 一节中介绍。

图 肆 演示了 Contacts Provider 在维系人数据流中所起的成效。 在标为“sync adapters”的虚线框中,各类适配器(Adapter)都标注了账户类型。

永利集团304手机版 4

图 4. Contacts Provider 数据流图。

必备的权能

访问 Contacts 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">

上述权限与用户个人资料(Profile)音信非亲非故。用户个人资料及权限将在 用户个人资料 1节中牵线。

请牢记,联系人音讯享有私密性和敏感性。 用户都会对比在意友好的难言之隐,所以必然不期望应用程序搜聚自己或其余关联人的新闻。 要是未有为访问联系人新闻提交丰富的理由,用户恐怕会对该选拔给出差评,可能直接拒绝安装。

用户个人资料

ContactsContract.Contacts 表中有一条记下封存着脚下用户的个人资料。 那条数据描述的是设备用户,而不是联络员。 那条 Contacts 数据与一条 Raw Contacts 的记录关联,每一个带有用户个人资料的体系都会有一条。 每条 Raw Contacts 记录能够对应当多条 Data 表的笔录。在 ContactsContract.Profile 类中提交了独具访问用户个人资料时要用到的常量定义。

做客用户个人资料需求一定的权力。除了读写联系人时索要的 READ_CONTACTSWRITE_CONTACTS 权限之外,读写用户个人资料还分别需求 READ_PROFILEWRITE_PROFILE 权限。

请记住,用户个人资料属于敏感数据。具备 READ_PROFILE 权限能够访问到用户的个人身份新闻。请务必在应用程序的叙说音讯里,表明申请用户个人资料访问权限的理由。

因而调用 ContentResolver.query() 方法,能够得到包蕴用户个人资料的 Contacts 记录。请把那边的 Content U本田UR-VI 置为 CONTENT_URI ,且无需付出任何查询条件。 基于该 Content URI ,还足以拿走 Raw Contact 记录及用户个人资料。 举个例子,以下代码段就兑现了用户个人资料的读取:

// 设置所要读取的用户个人资料字段
mProjection = new String[]
    {
        Profile._ID,
        Profile.DISPLAY_NAME_PRIMARY,
        Profile.LOOKUP_KEY,
        Profile.PHOTO_THUMBNAIL_URI
    };

// 从 Contacts Provider 中读取用户个人资料
mProfileCursor =
        getContentResolver().query(
                Profile.CONTENT_URI,
                mProjection ,
                null,
                null,
                null);

注意: 假如读取到多条关系人记录,通过检查 IS_USER_PROFILE 字段,能够鲜明哪一条是用户个人资料数据。 借使该字段为 一,则意味该条记录为用户个人资料。

Contacts Provider 元数据

Contacts Provider 用数据库管理着联系人的数量。 这几个元数据(Metadata)分别寄存于几张表中,包罗 Raw Contacts、Data、Contacts、 ContactsContract.SettingsContactsContract.SyncState 等。各样元数据的功能如下所示:

表 3.Contacts Provider 中的元数据

表名

字段

含义

ContactsContract.RawContacts

DIRTY

“0”表示自前一遍联合以来从未成形

标记本机 Raw Contact 记录已被修改过并供给联合到劳动器端去。 当 Android 应用程序做出修改后,Contacts Provider 会自动安装该字段值。

修改 Raw Contact 或 Data 表的 Sync Adapter 应该保险在 Content UCRUISERI 前面增多 CALLER_IS_SYNCADAPTER 字符串。那样会阻拦 Provider 把该条记录标识为 DIRTY 。 不然的话, Sync Adapter 修改也会被视为本地修改,并发送至服务器端,但骨子里这种修改正是由服务器端发起的。

“一”表示前二回联袂之后发出了变通,须求将数据向劳动器端同步。

ContactsContract.RawContacts

VERSION

行当数据的版本号。

设若产业数据或关系记录发生了变动, Contacts Provider 就能够自行递增该字段值。

ContactsContract.Data

DATA_VERSION

行当数据的版本号。

倘诺那些 Data 数据发生了调换,Contacts Provider 就能自行递增该字段值。

ContactsContract.RawContacts

SOURCE_ID

字符串值,唯1标记了创设该条 Raw Contact 记录的账户。

当 Sync Adapter 新建一条 Raw Contact 记录时,本字段就应被置为服务器端给出的唯壹 ID。 而当 Android 应用程序新建一条 Raw Contact 记录时,应该将本字段保持为空。 那就象征, Sync 艾达pter 应该先在服务器端创制一条 Raw Contact 记录,并为 SOURCE_ID 获取1个值。

有某个特地重大,各类账户类型的 SOU奥迪Q7CE_ID 必须唯一,并在联合进度中维系不改变:

  • 唯一性:各种账户对应的 Raw Contact 记录都必须怀有唯一的 SOUEscortCE_ID。 如若做不到那或多或少,“联系人”应用就能够出错。 请注意,账户类型自始至终的 Raw Contact 记录能够享有同样的 SOU大切诺基CE_ID。 比如,账户 emily.dickinson@gmail.com 对应的 Raw Contact 为“Thomas Higginson”, 它的 SOURCE_ID 就足以和 emilyd@gmail.com 对应的 Raw Contact 记录“Thomas Higginson”相同。
  • 稳定性:SOURCE_ID 是 Raw Contact 位于在线服务端的长久性数据。 例如:假如用户在应用程序设置中解决了关系人新闻,然后又举办了一回联袂, 那么恢复生机回来的 Raw Contact 记录仍旧会保持原本的 SOU奥迪Q7CE_ID。 若是做不到那一点,联系人的急迅格局将会失效。

ContactsContract.Groups

GROUP_VISIBLE

“0”表示本组联系人不允许展现在 Android 应用程序的分界面中。

本字段是为了与有个别服务器端保持相当,那些服务端协理隐藏某组联系人的功用。

“一”表示本组联系人可由应用程序展现。

ContactsContract.Settings

UNGROUPED_VISIBLE

“0”表示:要是不属于任何组,那么本账户和账户类型的关系人将不会显得在 Android 应用程序分界面中。

暗中认可情况下,假使联系人的保有“Raw Contact”均不属于其余群组( Raw Contact 的分组关系由 ContactsContract.Data 表中的 ContactsContract.CommonDataKinds.GroupMembership 记录来定义),那么那一个关系人是不可知的。通过安装 ContactsContract.Settings 表中的那个字段,可以强制展现某账户类型及账户的未分组联系人。 本标识的壹种用途就是把劳务器端未分组的联络员显示出来。

“壹”表示:即使不属于任何组,本账户和账户类型的关联人也得以在 Android 应用程序分界面中显示。

ContactsContract.SyncState

(全部字段)

本表供 Sync 艾达pter 存放元数据。

本表可用于在本地悠久保存同步状态及此外有关数据。

访问 Contacts Provider

本节介绍了 Contacts Provider 的拜访规则,珍视包括:

  • 实业查询。
  • 批量改造。
  • 透过 Intent 读取和改造数据。
  • 数据完整性。

至于由 Sync Adapter 实行数据修改的越来越多细节,还将要 Contacts Provider Sync Adapter 一节中张开介绍。

询问实体

因为 Contacts Provider 中的数据表是安份守己一定的档期的顺序结构协会在联合的,所以它非常适用于将一条记下连同全部关乎“子”记录一齐读收取来。 举例,为了展现某人的具有音讯,或者要读取一条 ContactsContract.Contacts 记录对应的具有 ContactsContract.RawContacts 记录,或许是一条 ContactsContract.RawContacts 记录对应的装有 ContactsContract.CommonDataKinds.Email 记录。为了便利操作,Contacts Provider 建议了 实体(Entity)的概念,它相仿于关联了多张表的数据库。

2个实体类似于一张表,它由某父表及其子表中的选定字段构成。 在对实业进行查询时,需求依靠实体的字段,给出字段映射关系(Projection)和询问条件。 再次来到的结果是三个 Cursor(游标),个中每一个子表的每条记下都对应着一条记下。 举个例子,如果查询了叁个 ContactsContract.Contacts.Entity ,要获得某些联系人姓名及该姓名下持有 Raw Contact 对应的有着 ContactsContract.CommonDataKinds.Email 记录,则赶回的结果中每一行都对应于一条 ContactsContract.CommonDataKinds.Email 记录。

实体简化了查询操作。利用实体能够一回取回某些联系人的具有音信,而无需先查询父表获取 ID、再用 ID 查询子表了。 而且 Contacts Provider 在管理实体查询时将放入贰个业务中来变成,确定保证了数码的1致性。

注意: 实体平日不会含有父表和子表的整整字段。要是计划对不在实体字段常量列表中的字段实行操作,将会触发异常

以下代码段演示了读取某联系人的有所 Raw Contact 记录。 这段代码属于3个颇具多个 Activity “main”、“detail”的应用程序。 Activity “main”将显得联系人列表,当用户选中一个关联人时,Activity 将其 ID 发送给 Activity “detail”。 Activity “detail” 利用 ContactsContract.Contacts.Entity 突显选中联系人的享有 Raw Contact 数据。

那是 Activity “detail” 的1对代码:

...
    /*
     * 在 URI 中添加实体路径。
     * 对于 Contacts Provider 而言, URI 应为 content://com.google.contacts/#/entity (# 代表 ID)。
     */
    mContactUri = Uri.withAppendedPath(
            mContactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);

    // 初始化 loader 。
    getLoaderManager().initLoader(
            LOADER_ID,  // loader ID
            null,       // loader 的参数(这里没有)
            this);      // Activity 的上下文 context

    // 新建 ListView 要绑定的 Cursor Adapter 
    mCursorAdapter = new SimpleCursorAdapter(
            this,                        // Activity 的 context
            R.layout.detail_list_item,   // 包含 detail widget 的 View 项
            mCursor,                     // 处于后台的游标
            mFromColumns,                // 游标中的数据字段
            mToViews,                    // 显示数据用的 View 
            0);                          // 标志

    // 设置 ListView 的后台 Adapter
    mRawContactList.setAdapter(mCursorAdapter);
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    /*
     * 设置要读取的字段。
     * RAW_CONTACT_ID 标识了当前数据行所属的 Raw Contact
     * DATA1 为 Data 记录中的第一个数据字段(通常存放最重要的数据)。
     * MIMETYPE 指明了 Data 记录的数据类型。
     */
    String[] projection =
        {
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
        };

    /*
     * 根据 Raw Contact 的 ID 对返回游标进行排序,
     * 以便让同一个 Raw Contact 的所有 Data 记录放在一起。
     */
    String sortOrder =
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID  
            " ASC";

    /*
     * 返回一个新的 CursorLoader。
     * 调用参数与 ContentResolver.query() 类似,只是 Context 参数不同,那里是所用的 ContentResolver。
     */
    return new CursorLoader(
            getApplicationContext(),  // Activity 的 Context
            mContactUri,              // 联系人的实体 Content URI
            projection,               // 要返回的字段
            null,                     // 读取所有 Raw Contact 记录及相关 Data 记录
            null,                     //
            sortOrder);               // 按照 Raw Contact ID 排序
}

载入达成后, LoaderManager 将会调用 onLoadFinished() 。该方法的传播参数中有一个暗含查询结果的 Cursor 。然后应用程序就足以从该 Cursor 中读取数据,用于体现或其余用途。

批量修改

相应尽量地以“批管理形式”(Batch)举办 Contacts Provider 的增加和删除改操作。 那通过成立由 ContentProviderOperation 组成的 ArrayList ,然后调用 applyBatch() 就能够达成。因为 Contacts Provider 会把1个 applyBatch() 中的全数操作放入五个作业中达成,所以不会生出多少区别样的状态。 批量修改也让贰回到位 Raw Contact 和细致记录的操作变得尤其轻巧。

注意: 就算要修改单条 Raw Contact 记录,可以设想向系统自带的联系人使用发送 Intent, 而不用在大团结的主次中来完毕。 详细情况请参阅通过 Intent 读写多少。

Yield Point

如果批量修改所含有的操作大多,就能够堵塞别的的过程,那样用户体验就能够很倒霉。 那时就需求把具有操作尽恐怕拆分为八个单身的列表,并幸免系统的梗塞,这能够经过设置Yield Point来实现。 Yield Point 是一种 ContentProviderOperation 对象,它的 isYieldAllowed() 设为 true。 当 Contacts Provider 管理到 Yield Point 时,将会有始无终操作让别的进度运维,并关闭当前事情。 当再度运营 Contacts Provider 时,它会持续 ArrayList 中的操作,并运营二个新职业。

Yield Point 使得每一回 applyBatch() 调用时会确立两个业务。由此,应该把插入 Raw Contact 记录和有关 Data 记录放在一同,再安装叁个 Yield Point。 或是把与1个联系人相关的操作结合在1块,再安装1个 Yield Point。

Yield Point 也是1种原子操作单位。七个 Yield Point 之间的操作依旧全体成功,要么全体输球。 假使未有设置任何 Yield Point,则极小的原子操作单位便是全体批量义务。 通过 Yield Point 的接纳,确实可避防止系统天性的降低,同有的时候间也把方方面面操作拆分为多少个原子操作组。

修改记录时的向前引用(Back Reference)

在用一组 ContentProviderOperation 插入 Raw Contact 及有关 Data 记录时, 必须把多条 Data 记录与该 Raw Contact 实行关联,那通过把 RAW_CONTACT_ID 字段值设为该 Raw Contact 的 _ID 就能够。可是,在开立插入 Data 记录的 ContentProviderOperation 时,该 ID 值还未稳当,因为此时插入 Raw Contact 记录的 ContentProviderOperation 还并没有交给呢。 为了缓慢解决这一难题,能够由此 ContentProviderOperation.Builder 类的 withValueBackReference() 方法。该办法允许行使前贰回操作的结果插入或更换字段。

withValueBackReference() 方法的参数有八个:

key
键-值对中的键(key)。本参数值应为要修改的字段名。

previousResult
数组索引,从0初始,该数组由 ContentProviderResult 对象组成,是由 applyBatch() 生成的。 当施行批量操作时,每步操作的结果都被保留在一当中级结果数组中。 previousResult 即为那么些中级结果的目录,可因此 key 进行读写。 那样就可以先插入一条 Raw Contact 记录并获取其 _ID 值,在继续插入 ContactsContract.Data 记录时就能够“向前引用”(Back Reference)该值。

中级结果数组是在率先次调用 applyBatch() 时创制的,数组大小相等由所需 ContentProviderOperation 组成的 ArrayList 大小。可是,该结果数组的具备因素都预置为 null,要是准备上前引用八个不设有的终止结果, withValueBackReference() 将会抛出 Exception

以下代码段演示了批量陈设 Raw Contact 及 Data 记录的经过。 个中包含了建立Yield Point 及选用向前引用的代码。这段代码是 ContactAdder 类的 createContacEntry() 方法的升迁版,该类属于 Contact Manager 例程的一片段。

首先段代码将从分界面中读取联系人音信。用户那时应该已经选取了要增多 Raw Contact 记录的账户。

// 根据用户界面中的信息,在当前选中账户中创建联系人入口
protected void createContactEntry() {
    /*
     * 读取界面中的数据
     */
    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());

以下代码成立了贰个 Operation 对象,其在 ContactsContract.RawContacts 表中插入一条 Raw Contact 记录。

     /*
     * 准备插入 Raw Contact 记录的批量操作。
     * 即便 Contacts Provider 中不存在此联系人的任何数据,也不允许直接添加 Contact 记录,
     * 而只能添加一条 Raw Contact 记录。
     * Contacts Provider 会随后自动生成一条  Contact 。
     */

     // 新建一个由 ContentProviderOperation 对象组成的队列
    ArrayList<ContentProviderOperation> ops =
            new ArrayList<ContentProviderOperation>();

    /*
     * 创建指定账户类型(服务器类型)和账户名称(用户名)的 Raw Contact 记录。
     * 请注意,账户的显示名称并不保存在此记录中,而是存于 StructuredName 记录中。
     * 其他数据可以不填。
     */
    ContentProviderOperation.Builder op =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());

    // 创建操作并加入到队列中
    ops.add(op.build());

接下去,创立展现名称、电话号码和 Email 记录。

每一个 Builder 对象都因而 withValueBackReference() 获得 RAW_CONTACT_ID。 那么些引用(Reference)指向第一步操作的结果对象 ContentProviderResult ,而首先步操作中增加了 Raw Contact 并回到新生成记录的 _ID。 那样,每条记下都因而投机的 RAW_CONTACT_ID 字段与所属的新扩张 ContactsContract.RawContacts 记录关联起来。

ContentProviderOperation.Builder 添加 Email 记录, withYieldAllowed() 标识表示设置二个政工提交点(yield point)。

    // 为新 Raw Contact 记录创建显示名称,即一条 StructuredName 记录。
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference 将第一个参数置为 ContentProviderResult 值,
             * ContentProviderResult 的索引值由第二个参数给出。
             * 在本例中,StructuredName 的 Raw Contact ID 列设为第一步操作返回的结果值,
             * 这步操作也就是实际添加 Raw Contact 记录的操作。
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // 把本条记录的 MIME 类型置为 StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // 将本条记录的显示名称设置为用户界面中显示的名字
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);

    // 生成操作对象并添加到队列中
    ops.add(op.build());

    // 插入电话号码,记录类型设置为 Phone 类型
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * 把 Raw Contact ID 字段置为第一步操作返回的 Raw Contact ID
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // 把 MIME 类型设为 Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // 设置电话号码和类型
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);

    // 生成操作对象并添加到队列中
    ops.add(op.build());

    // 插入 Email 数据,类型设为 Email
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * 把 Raw Contact ID 字段置为第一步操作返回的 Raw Contact ID
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // 把 MIME 类型设为 Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // 设置 Email 值和类型
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);

    /*
     * 演示事务提交点(yield point)。
     * 这表示在本次插入操作完成后,批量操作线程将优先于其他线程执行。
     * 每对一个联系人完成一组操作后,请设置一个提交点,以避免(维持长事务带来的)性能下降。
     */
    op.withYieldAllowed(true);

    // 生成操作并加入操作队列
    ops.add(op.build());

末段一段代码演示了 applyBatch() 的调用,以便插入新 Raw Contact 及数量。

    // 请求 Contacts Provider 新建一个联系人
    Log.d(TAG,"Selected account: "   mSelectedAccount.getName()   " ("  
            mSelectedAccount.getType()   ")");
    Log.d(TAG,"Creating contact: "   name);

    /*
     * 批量提交 ContentProviderOperation 队列。
     * 忽略返回结果。
     */
    try {

            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
    } catch (Exception e) {

            // 显示警告
            Context ctx = getApplicationContext();

            CharSequence txt = getString(R.string.contactCreationFailure);
            int duration = Toast.LENGTH_SHORT;
            Toast toast = Toast.makeText(ctx, txt, duration);
            toast.show();

            // 将异常记入日志
            Log.e(TAG, "Exception encountered while inserting contact: "   e);
    }
}

批量操作能够完成开朗并发调控(optimistic concurrency control), 这种方法能够不必锁定底层数据就能够达成修改专门的学问。 为了选择这种措施,在付出业务后,必要检讨可能同有的时候候发出的任何改换操作。 假设开采了修改争执,供给回滚并再一次提交。

乐天并发调整在活动器具上充足有用,因为与此同不时候只会有2个用户,同临时间做客一块数据的或然相当的小。 因为未有使用锁定机制,就无需浪费时间加锁,也不需求等待其余作业解锁了。

一旦要在更新某条 ContactsContract.RawContacts 数据行时使用乐观并发调控,请按以下步骤实践:

  1. 读取数据时,同不时候取回 Raw Contact 的 VERSION 字段。
  2. 依据差异的威迫约束原则,用 newAssertQuery(Uri) 方法成立三个正好的 ContentProviderOperation.Builder 对象。对于 Content URI 而言,使用 RawContacts.CONTENT_URI ,并附带 Raw Contact ID 即可。
  3. 调用 ContentProviderOperation.Builder 对象的 withValue() 方法,把 VERSION 字段与前方取回的版本号进行自己检查自纠。
  4. 再调用此 ContentProviderOperation.Builder 对象的 withExpectedCount() 方法,确定保障本次相比较只涉嫌一条记下。
  5. 调用 build() 方法创造 ContentProviderOperation 对象,并把它看做第二个分子进入列表 ArrayList 中,这一个列表是要传给 applyBatch() 的。
  6. 付出批处监护人务。

假使在读写某 Raw Contact 记录时期,别的操作也在创新此记录,“断言”(assert) ContentProviderOperation 将会失利,全体批量操作都会收回。 前边能够另行提交可能举办别的操作。

以下代码演示了,在用 CursorLoader 查询到一条 Raw Contact 记录后, 怎样成立 ContentProviderOperation “断言”:

/*
 * 应用程序通过 CursorLoader 查询 Raw Contacts 表。
 * 系统将会在加载完成后调用此方法。
 */
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    // 读取 Raw Contact _ID 和 VERSION 值
    mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}

...

// 为断言操作建立 Uri
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);

// 创建断言操作
ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);

// 添加断言:检查版本号、涉及的记录数
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);

// 创建 ArrayList 保存 ContentProviderOperation 对象
ArrayList ops = new ArrayList<ContentProviderOperationg>;

ops.add(assertOp.build());

// 可往 ops 中添加其他批量操作

...

// 提交批处理操作。如果断言失败,将会抛出异常
try
    {
        ContentProviderResult[] results =
                getContentResolver().applyBatch(AUTHORITY, ops);

    } catch (OperationApplicationException e) {

        // 在这里完成断言失败时要执行的动作
    }

因而 Intent 读写多少

因此向系统的 Contacts 应用发送 Intent,能够直接访问 Contacts Provider. 该类 Intent 将开拓 Contacts 应用的分界面,用户能够在里面举办一些联络人消息的操作。 这种措施得以让用户张开:

  • 从列表中精选联系人并向调用方应用回到相关数据
  • 编排已有挂钩人新闻
  • 在用户账号下新建 Raw Contact 记录
  • 除去联系人或有关数据

假使用户必要插入或退换数据,能够先记下那一个新闻并视作 Intent 的1局地发送出去。

在用 Intent 通过系统 Contacts 应用访问 Contacts Provider 时, 不须要统一筹划用户分界面,没有须要编写制定数据访问代码,也无须申请 Provider 读写权限。 Contacts 系统利用能够赋予读取联系人的权位, 因为是因此任何应用程序修改 Provider 数据的,所以也无需持有写入权限。

发送 Intent 来访问 Provider 的通用步骤在 Content Provider 基础 的“通过 Intent 访问数据”壹节中已有详细介绍。 相关操作可用的 Action 的 MIME 类型,以及数据类型都在表四中列出, putExtra() 可用的附属类小部件数据都在参谋文书档案 ContactsContract.Intents.Insert 中给出。

表 4. Contacts Provider Intent

操作 Action 数据 MIME 类型 备注
选取联系人 ACTION_PICK
  • Contacts.CONTENT_URI, 显示联系人列表
  • Phone.CONTENT_URI, 显示某个 Raw Contact 的电话号码列表
  • StructuredPostal.CONTENT_URI, 显示某个 Raw Contact 的邮寄地址列表
  • Email.CONTENT_URI, 显示某个 Raw Contact 的 Email 地址列表
之一
不需要 根据给出的 Content URI 类型,显示 Raw Contact 列表或某个 Raw Contact 的数据列表。

调用 startActivityForResult() ,返回选中行的 Content URI。 URI 的格式是数据表的 URI 加数据行的LOOKUP_ID 。 在调用者 Activity 的存活期间,系统“联系人”应用会授予其对该 URI 的读写权限。 详情请参阅文档 Content Provider 基础

插入新 Raw Contact 记录 Insert.ACTION N/A RawContacts.CONTENT_TYPE ,表示一组 Raw Contact 。 显示系统“联系人”应用的新建联系人窗口。 加入 Intent 中的附件数据将一起显示出来。 如果是用 startActivityForResult() 发送的,新 Raw Contact 记录的 Content URI 将会传回给 Activity 的 onActivityResult() 方法,在 Intent 参数的“data”部分中。调用 getData() 即可读取。
编辑联系人 ACTION_EDIT 联系人的 CONTENT_LOOKUP_URI 。用户可以在编辑器窗口中修改联系人相关的数据。 Contacts.CONTENT_ITEM_TYPE ,表示一个联系人。 显示 Contacts 应用中的“修改联系人”窗口。加入 Intent 中的附件数据将会一并显示出来。 用户点击保存按钮保存数据时,调用者的 Activity 将会回到前台。
显示可添加数据的选择列表 ACTION_INSERT_OR_EDIT N/A CONTENT_ITEM_TYPE 这个 Intent 总是显示 Contacts 应用的选择界面。 用户可以选中某个联系人进行编辑,也可以添加新的联系人。 到底是显示编辑还是添加界面,取决于用户的选择,以及用 Intent 附件显示的信息。 如果调用方显示的是联系人的相关数据,比如 Email 或电话号码,则可利用此 Intent 让用户为已有联系人添加数据。

注意:在此类 Intent 附件中不需要发送姓名, 因为用户要么是从已有姓名中选取一个,要么就是添加新用户。 而且,假如发送了姓名且用户选择了编辑联系人,则 Contacts 应用会显示发送过去的姓名,之前的姓名会被覆盖。 如果用户没注意到这一点,又进行了保存,则以前的姓名就丢失了。

系统“联系人”应用不容许通过 Intent 删除 Raw Contact 及连锁数据。 要想删除 Raw Contact 记录,请使用 ContentResolver.delete()ContentProviderOperation.newDelete()

以下代码演示了何等树立并发送2个插入 Raw Contact 及数量的 Intent:

// 读取用户界面中的数据
String name = mContactNameEditText.getText().toString();
String phone = mContactPhoneEditText.getText().toString();
String email = mContactEmailEditText.getText().toString();

String company = mCompanyName.getText().toString();
String jobtitle = mJobTitle.getText().toString();

// 新建一个发送给系统“联系人”应用的 Intent
Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);

// 把 MIME 类型置为所需的插入记录 Activity
insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);

// 设置联系人姓名
insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);

// 设置公司名称和职位
insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);

/*
 * 以列表方式添加各数据行,相互以 DATA 键关联
 */

// 定义 ContentValues 对象列表,每个对象对应一行数据
ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();


/*
 * 定义 Raw Contact 行
 */

// 新建 ContentValues 对象作为行数据
ContentValues rawContactRow = new ContentValues();

// 加入账户类型和名称
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());

// 把此行添加到列表中
contactData.add(rawContactRow);

/*
 * 建立电话号码数据行
 */

// 新建 ContentValues 对象作为行数据
ContentValues phoneRow = new ContentValues();

// 设定 MIME 类型(所有数据行都必须给定类型)
phoneRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
);

// 加入电话号码及其类型数据
phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);

// 把此行添加到列表中
contactData.add(phoneRow);

/*
 * 建立 Email 数据行
 */

// 新建 ContentValues 对象作为行数据
ContentValues emailRow = new ContentValues();

// 设定 MIME 类型(所有数据行都必须给定类型)
emailRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
);

// 加入 Email 及其类型数据
emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);

// 把此行添加到列表中
contactData.add(emailRow);

/*
 * 把上述列表添加到 Intent 的附件中。
 * 这个列表必须是可序列化的(parcelable),以便能在进程间传递。
 * 系统应用 Ccontacts 需要把 Intents.Insert.DATA 内容作为键值使用。
 */
insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);

// 发送 Intent,启动系统“联系人”应用并打开新建联系人 Activity。
startActivity(insertIntent);

数据完整性

因为沟通人数量相比关键和机智,用户期望能即时创新,所以 Contacts Provider 规定了部分有限支撑数据完整性的条条框框。 在修改联系人消息时,必须信守那个规则。 下边给出一些相比较根本的平整:

为每条新建 ContactsContract.RawContacts 记录都增加一条 ContactsContract.CommonDataKinds.StructuredName 记录。
在归并联络人多少时,倘若 ContactsContract.RawContacts 数据行未有在 ContactsContract.Data 表中存在对应的 ContactsContract.CommonDataKinds.StructuredName 记录,大概会引发难题。

自然要把 ContactsContract.Data 链接到上级 ContactsContract.RawContacts 记录中。
向来不链接到 ContactsContract.RawContacts 记录的 ContactsContract.Data 数据行不会在系统“联系人”应用中浮现,并只怕会在运用 Sync Adapter 时引发难题。

只修改自有 Raw Contact 的多少。
请牢记,Contacts Provider 平常管理着广大两样账户类型/在线服务的数码。 必须确定保证只对友好的数额行举行修改或删除操作,且只用本人的账户类型和户名插入数据。

在指定 Authority、Content URI、URI Path、字段名、MIME 类型及 TYPE 时,确认保证使用 ContactsContract 及其子类中定义的常量,
选拔这一个常量有助于防止失误。 还是能够在常量过时后接受编写翻译器的警戒音信。

自定义数据

通过成立并行使自定义的 MIME 类型,能够在 ContactsContract.Data 表中插入、编辑、删除和读取自定义数据。 纵然能够把自定义类型的字段名映射为默许字段名称,自定义数据行只好利用 ContactsContract.DataColumns 中给定的字段。在系统“联系人”应用中,自定义数据能够平常显示,但非常小概编辑和删除,用户也不可能进入其余音讯。 假如要让用户能改改自定义数据行,必须自行提供编辑 Activity。

为了显示自定义数据,要求提供1个 contacts.xml 文件,里面富含三个 <ContactsAccountType> 成分,当中间含有1个以上的 <ContactsDataKind> 子成分。 详细情况请参阅 <ContactsDataKind>元素 一节。

有关自定义 MIME 类型的越来越多细节,请参阅 创建 Content Provider。

Contacts Provider Sync Adapter

为了形成设备和在线服务期间的维系人多少同步(synchronization),Contacts Provider 经过了特意规划。 那样,用户就可以把已有数量下载到新装置上,仍是可以够把数据上传到新建账户中去。 同步能够确定保证用户手头的器械具备新型的数目,不管是出于增添如故修改引起的。 同步的另一个便宜便是,在网络断开时设备也能动用联系人音讯。

固然达成同台的办法得以有诸四种,但 Android 系统提供了1种插件式(plug-in)的联手框架,能够自行达成以下职业:

  • 反省互连网是还是不是可用;
  • 听他们说用户安装安插同步陈设;
  • 重启已截至的共同。

选取那1框架时,须求提交 Sync Adapter 插件。 各类 Aync Adapter 都唯一对应贰个劳务和贰个 Content Provider,但足以拍卖同1个服务里的七个账户名。 此框架还允许同1套服务和 Provider 使用多少个 Sync 艾达pter。

Sync Adapter 类和文书

Sync Adapter 都实现为 AbstractThreadedSyncAdapter 的二个子类,并要作为 Android 应用的壹局地开始展览设置。 系统会从 Manifest 文件中获得 Sync Adapter 的消息,并读取由 Manifest 文件提交的 XML 文件。 那几个 XML 文件定义了在线服务的账户类型,以及 Content Provider 的用户认证新闻,那一个都以该 Adapter 的唯一标记。 Sync Adapter 一初始并不会运营,唯有当用户增加 Sync Adapter 中账户类型的账户,并张开其对应 Content Provider 的联合签字时,它才会被激活。 那时,系统会负担管理 Adapter,适时调用它产生 Content Provider 和服务器之间的1道。

注意: 把账户类型作为 Sync Adapter 唯一标志的一局地,能够让系统对它们举行分组,把走访同壹集团服务的 Sync Adapter 放在一块儿。 比方,谷歌(Google) 在线服务的 Sync Adapter 都存有同等的账户类型 com.google。 当用户在配备上增添 谷歌(Google)账户时,全体已安装的 谷歌(Google) 服务 Sync Adapter 都会议及展览示在共同; 每种 Sync Adapter 列出各自关联的地头 Content Provider。

因为繁多劳务都急需在拜访数据之前验证用户地方,Android 系统提供了1种与 艾达pter Adapter 框架好像的,并与其协同专门的学问的用户认证框架。 这种认证框架使用了插件式的 Authenticator,它是 AbstractAccountAuthenticator 的三个子类。Authenticator 验证用户地点的步骤如下:

  1. 记录用户名、密码或附近消息(用户证书);
  2. 向服务发送用户认证音讯;
  3. 检查服务重返结果。

即使服务接受了用户消息,Authenticator 能够保留那个音信以备现在选用。 因为认证框架是插件式的, AccountManager 能够访问具备可支撑的令牌(authtoken),并选取其公开性,比方 OAuth二令牌。

尽管如此用户认证进度不是不可缺少环节,但超过二分之1与“联系人”相关的劳务都会必要选拔。 当然,这不一定非要用 Android 认证框架来完毕。

实现 Sync Adapter

为了给 Contacts Provider 编写二个 Sync Adapter,请先创设三个饱含以下一些的 Android 应用程序:

Service 组件,响应系统一发布出的绑定 Sync Adapter 的呼吁。
系统开头共同一时间,会调用服务的 onBind() 方法来获取3个 Sync Adapter 使用的 IBinder 。那使得系统能够跨进程调用 Adapter 的主意。

在Sync Adapter 范例 中,服务的类名称为 com.example.android.samplesync.syncadapter.SyncService

Sync Adapter 实体,实现为 AbstractThreadedSyncAdapter 的实体类。
此类完结从服务器下载数据、上传本机数据、协调争持等工作。 艾达pter 的机要工作在 onPerformSync() 方法中。此类必须达成为单实例。

在 Sync Adapter 范例 中,Sync Adapter 定义在 com.example.android.samplesync.syncadapter.SyncAdapter中。

Application 的子类。
该类当作 Sync Adapter 单实例的厂子类来使用。通过 onCreate() 方法实例化 Sync Adapter,并提供静态方法“getter”用于向 onBind() 方法重临该单实例。 service.

可选: 响应系统倡导的用户认证请求的 Service 组件。
AccountManager 运营此服务起始认证进程。该服务的 onCreate() 方法实例化一个 Authenticator 对象。 当系统供给证实 Sync Adapter 所用的用户账户时,将会调用其 onBind() 方法来赢得一个 IBinder。 那样系统就能够跨进度调用 Authenticator 的主意了。

在 Sync Adapter 范例 中,服务的类名称叫 com.example.android.samplesync.authenticator.AuthenticationService

可选: AbstractAccountAuthenticator 的实体子类,用于拍卖认证请求。
此类提供由 AccountManager 调用的主意,用来与服务器端实行身份验证。 由于劳动器端选用的技巧区别,各类评释进度的底细差距相当大。 关于用户认证的更加多内容,请参见服务器端所用软件的文书档案。

在Sync Adapter范例 中,Authenticator 在 com.example.android.samplesync.authenticator.Authenticator 类中定义。

概念 Sync Adapter 和 Authenticator 用到的 XML 文件,用于向系统实行宣示。
最近介绍的 Sync Adapter 和 Authenticator 服务组件定义于 Manifest 文件的 <service> 元素中。这几个要素包涵了以下部分 <meta-data> 子元素,用于向系统报告相应的数量音讯:

  • Sync Adapter 服务的 <meta-data> 成分指向了 XML 文件 res/xml/syncadapter.xml。 该文件相继定义了要与 Contacts Provider 同步的长距离服务 URubiconI 及服务的账户类型。
  • 可选: Authenticator 的 <meta-data> 成分指向了 XML 文件 res/xml/authenticator.xml。 该公文相继定义了 Authenticator 辅助的账户类型、认证进程中用于显示界面包车型客车 UI 财富。 这里的账户类型必须与上述 Sync Adapter 账户类型相平等。

社沟通数据

ContactsContract.StreamItemsContactsContract.StreamItemPhotos 表管理着来自社交互联网的数目。 能够编写制定1个 Sync Adapter,把来自个人社交互联网圈的数码增进到那两张表中去,也许从表中读取社交数据并出示出来。 通过这种方式,可以把团结的交际互连网后台服务和前台应用,与 Android 的应酬网络用户体验集成在同步。

社交换文字

社沟通数据项必须与某些 Raw Contact 关联。 RAW_CONTACT_ID 即为 Raw Contact 的 _ID 值。 Raw Contact 的账户类型和账户名称也会保存在社交换数据记录中。

社沟通数据保存在以下字段中:

ACCOUNT_TYPE
必填项。 本条社沟通数据的 Raw Contact 账户类型。在插入数据时务必安装。

ACCOUNT_NAME
必填项。 本条社交流数据的 Raw Contact 账户名称。在插入数据时务必设置。

ID 字段
必填项。 插入社调换数据时,必须插入以下 ID 字段:

  • CONTACT_ID :本条数据涉嫌的牵连人 _ID
  • CONTACT_LOOKUP_KEY :本条数据涉嫌的沟通人 LOOKUP_KEY
  • RAW_CONTACT_ID :本条数据涉嫌的 Raw Contact _ID

COMMENTS
可选项。保存概要音信,前缀于本条社交换数据突显。

TEXT
社沟通数据项的标题,能够是由数据源发送过来的,也足以是表达怎么着生花费条数据的音信。 该字段能够包括任特性式的数额,可停放能被 fromHtml() 分析的图片能源。Provider 大概会截断或略去超长的公文,但会尽量幸免在语言符号(tag)中间截断。

TIMESTAMP
文本字符串,表示本条社调换数据的插入或改造时间,单位是 Epoch 纪元(译者注:壹九陆玖-01-0壹 00:00:00 UTC)以来的毫秒数。 应用程序插入或修改本条数据时,Contacts Provider 会自动更新该字段。

为了能明显地展现社沟通数据,会用到 RES_ICONRES_LABELRES_PACKAGE ,这一个都意味着应用程序的能源。

ContactsContract.StreamItems 表还富含 SYNC1SYNC4 字段,用于 Sync Adapter 间的排挤同步。

社沟通图片

ContactsContract.StreamItemPhotos 表存放着社调换数据项关系的图形音讯。并经过 STREAM_ITEM_ID 字段与 ContactsContract.StreamItems表的 _ID 字段关联。图片的引用格局保存在以下字段中:

PHOTO 字段(BLOB类型)。
图表的2进制数据,Provider 对其进行了缩放以便保存和体现。 此字段是为了保障向后包容性才保留的,以前的 Contacts Provider 会使用那几个字段保存图片。 不过在今天的本子中,不应再利用那几个字段保存图片了。 而应利用 PHOTO_FILE_IDPHOTO_URI (都在下①节介绍)保存到文件中。 如今该字段用于存放图片的缩略图,以供读取。

PHOTO_FILE_ID
Raw Contact 相关图片的数字型 ID。 把此字段值附加在 DisplayPhoto.CONTENT_URI 常量之后,即为指向某些图片文件的 Content UMuranoI,然后调用 openAssetFileDescriptor() 就能够获得图片文件的句柄。

PHOTO_URI
直白针对本条数据对应图片文件的 Content UCRUISERI。 用此 U昂CoraI 调用 openAssetFileDescriptor() 能够拿走图片文件的句柄。

社交换数据表的利用

上述表的施用办法与 Contacts Provider 中的别的主表基本同样,以下几点除了这一个之外:

  • 那个表供给极度的造访权限。 读取时必要 READ_SOCIAL_STREAM 权限。修改时要求 WRITE_SOCIAL_STREAM 权限。
  • 对于种种 Raw Contact, ContactsContract.StreamItems 表中对应的记录数是有限量的。 要是到达上限,Contacts Provider 会自动删除 TIMESTAMP 最早的记录,以便为新进的社交流数据腾出空间。 用 Content U普拉多I 为 CONTENT_LIMIT_URI 举行数据库查询,能够获得记录数上限值,别的参数都置为 null 就能够。该查询会重临包括一条记下的 Cursor,且唯有三个字段 MAX_ITEMS

ContactsContract.StreamItems.StreamItemPhotos 类定义了 ContactsContract.StreamItemPhotos 的子表,里面存放着某条社沟通数据相关的图样数据记录。

社交换互动

Contacts Provider 管理的社沟通数据,连同系统“联系人”应用一同, 能够将社交网络类别与现存的牵连人连接起来,得以完结以下庞大功能:

  • 由此 Sync Adapter 将社交互联网服务与 Contacts Provider 同步数据, 能够读取联系人近些日子使用过的 Activity 并保留到 ContactsContract.StreamItemsContactsContract.StreamItemPhotos 表中,以备后用。
  • 除了那些之外常规的1块儿之外,还足以在用户点选并查阅某些联系人时,触发自有 Sync Adapter 读取附加新闻。 能够让 Sync Adapter 读取高分辨率头像图片,以及此人最新的社调换数据。
  • 透过在系统“联系人”应用和 Contacts Provider 中注册通告(notification), 能够在联络人被查看时,或是后台服务在修改联系人音信时,收到3个Intent。 与用 Sync Adapter 进行完全同步相比较,这种方法大概越发快捷,占用的带宽也越来越小。
  • 当用户在系统“联系人”应用中浏览时,可以把某部联系人增多到自建社交网络服务中去。 利用“邀约联系人”功效就可以成功,这里要求一个增加已有联系人的 Activity 和一个 XML 文件, 该 XML 文件提交了系统“联系人”应用和 Contacts Provider,以及自行建造应用的详细新闻。

Contacts Provider 与社调换数据的期限联合功用与别的一起是同样的。 关于联合的越来越多细节,请参阅 Contacts Provider Sync Adapter。 上边两节将介绍如何注册布告和诚邀联系人。

挂号并拍卖社交互连网数据查看请求

当用户查看由 Sync Adapter 管理的关系人时,为了能吸收接纳到通报,须要注册 Sync 艾达pter。步骤如下:

  1. 在等级次序的 res/xml/ 目录中,成立名称叫 contacts.xml 的文件. 借使该文件已存在,能够跳过此步。
  2. 在此文件中进入 <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> 成分。要是此因素已存在,则跳过此步。
  3. 只要供给登记服务,以便在用户展开系统“联系人”应用中的联系人详细情形页时,能接收通告, 请在上述 XML 成分中增多 viewContactNotifyService="serviceclass" 属性, serviceclass 是服务类的一点一滴限定格式名称,该服务用于吸收接纳来自系统“联系人”应用的 Intent。 对于通报(notifier)服务,能够应用 IntentService 的子类,以便服务抽取到 Intent。 接收到的 Intent 中隐含了用户所选 Raw Contact 的 Content U路虎极光I。 能够绑定该布告服务,并调用 Sync Adapter 更新该 Raw Contact 的数据。

若是要求在用户点击有些社交数据或图表时,调用某个Activity,请按以下步骤注册 Activity:

  1. 在品种的 res/xml/ 目录下,创制名字为 contacts.xml 的文件. 假若该文件已存在,能够跳过此步。
  2. 在此文件中参与 <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> 成分。假如此因素已存在,则跳过此步。
  3. 只要急需注册 Activity,以便当用户点击系统“联系人”应用中的社交数据项时,能进行持续管理, 请在上述 XML 成分中加多 viewStreamItemActivity="activityclass" 属性, activityclass 是 Activity 类的完全限定格式名称, 该 Activity 将用于吸收接纳来自系统“联系人”应用的 Intent。
  4. 倘使急需注册 Activity,以便当用户点击系统“联系人”应用中的社交图片时,能张开后续管理, 请在上述 XML 元素中增多 viewStreamItemPhotoActivity="activityclass" 属性, activityclass i是 Activity 类的一点壹滴限定格式名称, 该 Activity 将用来吸收接纳来自系统“联系人”应用的 Intent。

关于 <ContactsAccountType> 成分的越来越多细节,就要 <ContactsAccountType> 元素 一节中牵线。

收到到的 Intent 将包括用户点击的社沟通数据项或图表的 Content U途乐I。 假若急需对文件数据和图片分为五个 Activity 实行管理,能够在3个文书中而且使用二日天性。

与自行建造社交互连网服务举行互动

没有要求离开系统的“联系人”应用,用户就足以约请有些联系沙加入自行建造的社交网址。 只要让系统“联系人”应用向自行建造 Activity 发送1个特约 Intent 就可以。 请按以下步骤操作:

  1. 在类型的 res/xml/ 目录下,创立名字为 contacts.xml 的文件. 假使该公文已存在,能够跳过此步。
  2. 在此文件中投入 <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> 成分。即使此因素已存在,则跳过此步。
  3. 加多以下属性:

    • inviteContactActivity="activityclass"
    • inviteContactActionLabel="@string/invite_action_label"

    activityclass 是摄取 Intent 的 Activity 的一点一滴限定类名。 invite_action_label 是个字符串,将要系统“联系人”应用的 Add Connection (译者注:没找到,难道是分享?)菜单中展现。

注意: ContactsSource 是不合时宜的标记,今后对应的是 ContactsAccountType

contacts.xml 参考

contacts.xml 文件包涵了一部分 XML 成分,用于调控自行建造 Sync Adapter、自行建造应用,与系统“联系人”应用、Contacts Provider 之间的相互。 上边介绍那几个成分:

<ContactsAccountType> 元素

<ContactsAccountType> 成分调整着自行建造应用与“联系人”应用之间的相互。 语法如下:

<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> 属性的前缀 android: 不是必需的。

属性:

inviteContactActivity
自行建造应用中某些 Activity 的完全限定类名,当用户在系统“联系人”应用中采取 Add connection 菜单时, 将开荒此 Activity。

inviteContactActionLabel
显示在 永利集团304手机版,Add connection 菜单中的字符串,表示 inviteContactActivity 定义的 Activity。 比方能够用“Follow in my network”。 这里能够利用字符串财富 ID。

viewContactNotifyService
以完全限定格式命名的收到文告用的自行建造服务类,当用户查看有些联系人时,此服务会接受到通告。 该公告由系统“联系人”应用发出,那样自行建造应用就能够尽量地推迟相比成本财富的多寡操作,唯有在实际必要时再实践多少读写职责。 比如,自行建造应用可以响应那一翻看联系人的通告,读取并展示该联系人的高分辨率头像和新型的社沟通数据。 越多细节将在 社沟通音信的相互 一节中牵线。 关于接受公告的劳动实例,请查看 SampleSyncAdapter 例程中的 NotifierService.java 文件。

viewGroupActivity
以完全限定格式命名的自行建造应用中的贰个 Activity,用于体现群组音信。 用户在系统“联系人”应用中点击该群组的标题后,该 Activity 就能展现出来。

viewGroupActionLabel
用作“联系人”应用中某些控件的标题来呈现,用户能够点击查阅自行建造应用中的联系人群组。

比如说,假定当地已安装了 谷歌 应用,且经过“联系人”应用对 谷歌(Google) 数据进行了一头, 则在“联系人”应用的群组(Groups)选项卡中,将会看到 谷歌 “圈子”以群组的款式显得出来。 如若点击 谷歌(Google) 圈子,就能够收看以群组的点子显示的天地职员。 谷歌 Logo会显示在顶上部分,点击Logo则会跳转到 谷歌(Google) 应用中去。 “联系人”应用正是通过 viewGroupActivity 来完结上述操作的,并把 viewGroupActionLabel 的值赋为 谷歌 的Logo。

此间能够选用字符串能源 ID。

viewStreamItemActivity
以完全限定格式命名的自行建造应用中的一个 Activity, 当用户在“联系人”应用中式点心击有个别 Raw Contact 的一条社交换数据时,将会调用该 Acitivity。

viewStreamItemPhotoActivity
以完全限定格式命名的自行建造应用中的一个 Activity, 当用户在“联系人”应用中式点心击有些 Raw Contact 的图样时,将会调用该 Acitivity。

<ContactsDataKind> 元素

<ContactsDataKind> 成分调节着自行建造应用的自定义数据行在“联系人”应用中的显示形式。 语法如下:

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

包含于:

<ContactsAccountType>

描述:

“联系人”应用能够行使此因素,把自定义数据行作为 Raw Contact 的明细数据之壹,一齐呈现出来。 <ContactsAccountType> 的每个 <ContactsDataKind> 子成分, 代表1种自定义数据行类型,此条数据是由自建Sync Adapter 向 ContactsContract.Data 表增添的。每种自定义 MIME 类型都亟需加上1个 <ContactsDataKind> 成分。 假设自定义数据行的多少没有要求展现出来,则没有供给增多该因素。

属性:

android:mimeType
自定义数据行的 MIME 类型,该行数据位于 ContactsContract.Data 表中。举例,记录联系人最新鸿基土地资金财产理地方的多寡行,就可用 vnd.android.cursor.item/vnd.example.locationstatus 作为 MIME 类型。

android:icon
在“联系人”应用中展现于自定义详细的情况旁边的图标,以 Android Drawable 资源的花样提交。 标记该条数据出自自行建造服务。

android:summaryColumn
从 Data 记录中读到的第二项数据(共有两项)所在字段的名称。 在表示该条数据的列表项上,该字段中的内容将会显得在第二行。 第一行事可挑选,用于体现摘要音讯。 请参阅 android:detailColumn

android:detailColumn
从 Data 记录中读到的第1项数据(共有两项)所在字段的称号。 在表示该条数据的列表项上,该字段中的内容将会来得在第3行。 请参阅 android:summaryColumn

Contacts Provider 的其余职能

除上述重大作用外,Contacts Provider 还提供了以下用于拍卖联系人数据的意义:

  • 关联人群组
  • 图形功效

交换人群组

Contacts Provider 能够把一些联系人标记为群组。 假如有个别账户对应的服务器要求维护群组,该账户类型对应的 Sync Adapter 应承担 Contacts Provider 和服务器之间的群组消息传送职业。 当用户向服务器增多二个新关系人,并把她名下二个新组时,Sync Adapter 必须将以此新组增添到 ContactsContract.Groups 表中。 Raw Contact 所属的一个或五个组使用 ContactsContract.CommonDataKinds.GroupMembership MIME 类型存储在 ContactsContract.Data 表内。

假诺自行建造 Sync 艾达pter 会将服务器中的 Raw Contact 数据增进到 Contacts Provider 中,且并未有用到群组, 那么要求告诉 Provider 呈现这部分数码。 请在管理增加账户操作的代码中,修改账户对应的 ContactsContract.Settings 记录,那条记下是由 Contacts Provider 加多的。 把该行数据的 Settings.UNGROUPED_VISIBLE 字段值置为一就可以。那样,固然未用到群组效能,Contacts Provider 也会让有关的牵连人数量保持可知。

沟通人图片

图形消息以记录的款式保留在 ContactsContract.Data 表中,MIME 类型为 Photo.CONTENT_ITEM_TYPE 。每行数据通过 CONTACT_ID 字段与图片所属 Raw Contact 的 _ID 字段关联。 ContactsContract.Contacts.Photo 类定义了叁个 ContactsContract.Contacts 子表,在那之中存放了维系人的主图片音讯,即该联系人主 Raw Contact 的主图片。同样, ContactsContract.RawContacts.DisplayPhoto 类也定义了1个 ContactsContract.RawContacts 子表,个中存放了 Raw Contact 主图片的消息。

ContactsContract.Contacts.PhotoContactsContract.RawContacts.DisplayPhoto 的仿照效法文书档案包括了读取图片音信的示范。 系统未提供读取 Raw Contact 主缩略图的帮手类,但可以查询 ContactsContract.Data 表来找到 Raw Contact 的主图片记录,查询条件为 Raw Contact 的 _IDPhoto.CONTENT_ITEM_TYPEIS_PRIMARY 字段。

社交换数据自己也说不定含有图表。这一个图片都保留在 ContactsContract.StreamItemPhotos 表中,社交流图片1节对该表举办了详尽介绍。

TAG标签:
版权声明:本文由永利集团304手机版发布于对外交流,转载请注明出处:最后一篇API译文,的资料进一步整合到新