介绍

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

    本指南介绍下列内容:

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

    本指南假定您了解 Android 内容提供程序的基础知识。如需了解有关 Android 内容提供程序的更多信息,请阅读 内容提供程序基础知识指南。 示例同步适配器示例应用是一个示例,展示如何使用同步适配器在联系人提供程序与 Google 网络服务托管的一个示例应用之间传送数据。

联系人提供程序组织

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

1

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

 

  •     ContactsContract.Contacts 表 表示不同联系人的行,基于聚合的原始联系人行。
  •     ContactsContract.RawContacts 表 包含联系人数据摘要的行,针对特定用户帐户和类型。
  •     ContactsContract.Data 表 包含原始联系人详细信息(例如电子邮件地址或电话号码)的行。

 

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

原始联系人

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

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

    重要的原始联系人列

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

1

    说明

    以下是关于 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”,她的设备上定义了以下三个用户帐户:

 

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

数据

    如前文所做的说明,原始联系人的数据存储在一个 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 表为一些列使用了描述性名称,为其他列使用了通用名称。 使用描述性名称的列的内容具有相同的含义,与行中数据的类型无关,而使用通用名称的列的内容则会随数据类型的不同而具有不同的含义。

    描述性列名称

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


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


    通用列名称

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

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

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

    类型专用列名称

    为便于处理特定类型行的列,联系人提供程序还提供了 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 类型,则可自由定义您的自定义类型专用的列名称,并随心所欲地使用这些列。

    下图(类型专用列名称和通用列名称)显示的是描述性列和数据列在 ContactsContract.Data 行中的显示情况,以及类型专用列名称“覆盖”通用列名称的情况

2

    类型专用列名称类

    下表(类型专用列名称类)列出了最常用的类型专用列名称类:

3

    联系人

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

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

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

    联系人提供程序通过 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列的格式无关。

    下图(联系人表、原始联系人表与详细信息表之间的关系)显示的是这三个主要表的相互关系。

4

来自同步适配器的数据

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

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

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

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

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

    如果您想将服务的数据传送到联系人提供程序,则需编写您自己的同步适配器。 联系人提供程序同步适配器部分对此做了更详尽的描述。

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

5

所需权限

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

    对一个或多个表的读取权限: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">。

    这些权限不适用于用户个人资料数据。下面的用户个人资料部分对用户个人资料及其所需权限做了阐述。

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

用户个人资料

    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_PRIMARY,Profile.LOOKUP_KEY,Profile.PHOTO_THUMBNAIL_URI};
// Retrieves the profile from the Contacts Provider
mProfileCursor =getContentResolver().query(Profile.CONTENT_URI,mProjection ,null,null,null);

    注:如果您要检索多个联系人行并想要确定其中一个是否为用户个人资料,请测试该行的 IS_USER_PROFILE 列。 如果该联系人是用户个人资料,则此列设置为“1”。

联系人提供程序元数据

    联系人提供程序管理用于追踪存储库中联系人数据状态的数据。 这些有关存储库的元数据存储在各处,其中包括原始联系人表行、数据表行和联系人表行、 ContactsContract.Settings 表以及 ContactsContract.SyncState 表。 下表(联系人提供程序中的元数据)显示的是每一部分元数据的作用:

6

联系人提供程序访问

    本节描述访问联系人提供程序中数据的准则,侧重于阐述以下内容:

    • 实体查询。

    • 批量修改。

    • 通过 Intent 执行检索和修改。

    • 数据完整性。

    联系人提供程序同步适配器部分也对通过同步适配器进行修改做了更详尽的阐述。

    查询实体

    由于联系人提供程序表是以层级形式组织,因此对于检索某一行以及与其链接的所有“子”行,往往很有帮助。 例如,要想显示某位联系人的所有信息,您可能需要检索某个 ContactsContract.Contacts 行的所有ContactsContract.RawContacts 行,或者检索某个 ContactsContract.RawContacts 行的所有ContactsContract.CommonDataKinds.Email 行。 为便于执行此操作,联系人提供程序提供了实体构造,其作用类似于表间的数据库连接。

    实体类似于一个表,由父表及其子表中的选定列组成。 当您查询实体时,需要根据实体中的可用列提供投影和搜索条件。 结果会得到一个 Cursor,检索的每个子表行在其中都有一行与之对应。 例如,如果您在 ContactsContract.Contacts.Entity 中查询某个联系人姓名以及该姓名所有原始联系人的所有 ContactsContract.CommonDataKinds.Email行,您会获得一个 Cursor,每个 ContactsContract.CommonDataKinds.Email 行在其中都有一行与之对应。

    实体简化了查询。使用实体时,您可以一次性检索联系人或原始联系人的所有联系人数据,而不必先通过查询父表获得ID,然后通过该 ID 查询子表。此外,联系人提供程序可通过单一事务处理实体查询,这确保了所检索数据的内部一致性。

    注:实体通常不包含父表和子表的所有列。 如果您试图使用的列名称并未出现在实体的列名称常量列表中,则会引发一个 Exception。

    以下代码段说明如何检索某位联系人的所有原始联系人行。该代码段是一个大型应用的组成部分,包含“主”和“详”两个 Activity。 主 Activity 显示一个联系人行列表;当用户选择一行时,该 Activity 会将其 ID 发送至详 Activity。 详 Activity 使用 ContactsContract.Contacts.Entity 显示与所选联系人关联的所有原始联系人中的所有数据行。

    以下代码段摘自“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 initializenull,       // Arguments for the loader (in this case, none)this);      // The context of the activity// Creates a new cursor adapter to attach to the list viewmCursorAdapter = new SimpleCursorAdapter(this,                        // the context of the activityR.layout.detail_list_item,   // the view item containing the detail widgetsmCursor,                     // the backing cursormFromColumns,                // the columns in the cursor that provide the datamToViews,                    // the views in the view item that display the data0);                          // flags// Sets the ListView's backing adapter.mRawContactList.setAdapter(mCursorAdapter);
...
@Override
public 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 contextmContactUri,              // The entity content URI for a single contactprojection,               // The columns to retrievenull,                     // Retrieve all the raw contacts and their data rows.null,                     //sortOrder);               // Sort by the raw contact ID.
}

    加载完成时,LoaderManager 会调用一个 onLoadFinished() 回调。此方法的传入参数之一是一个 Cursor,其中包含查询的结果。在您自己的应用中,您可以从该 Cursor 获取数据,以进行显示或做进一步处理。

    批量修改

    您应尽可能地通过创建一个 ContentProviderOperation 对象 ArrayList 并调用 applyBatch(),以“批处理模式”在联系人提供程序中插入、更新和删除数据。 由于联系人提供程序是在 applyBatch() 中通过单一事务执行所有操作,因此您的修改绝不会使联系人存储库出现不一致问题。 此外,批量修改还有便于同时插入原始联系人及其明细数据。

    注:要修改单个原始联系人,可以考虑向设备的联系人应用发送一个 Intent,而不是在您的应用中处理修改。通过 Intent 执行检索和修改部分对此操作做了更详尽的描述。

    屈服点

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

    屈服点会导致每次调用 applyBatch() 会产生多个事务。因此,您应该为针对一组相关行的最后一项操作设置屈服点。 例如,您应该为一组操作中添加原始联系人行及其关联数据行的最后一项操作,或者针对一组与一位联系人相关的行的最后一项操作设置屈服点。

    屈服点也是一个原子操作单元。两个屈服点之间所有访问的成功或失败都将以一个单元的形式出现。 如果您不设置任何屈服点,则最小的原子操作是整个批量操作。 如果您使用了屈服点,则可以防止操作降低系统性能,还可确保一部分操作是原子操作。

    修改向后引用

    当您将一个新原始联系人行及其关联的数据行作为一组 ContentProviderOperation 对象插入时,需要通过将原始联系人的 android.provider.BaseColumns#_ID 值作为 RAW_CONTACT_ID 值插入,将数据行链接到原始联系人行。 不过,当您为数据行创建 ContentProviderOperation 时,该值不可用,因为您尚未对原始联系人行应用ContentProviderOperation。 为解决此问题, ContentProviderOperation.Builder 类使用了 withValueBackReference() 方法。 该方法让您可以插入或修改包含上一操作结果的列。

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

  •     key:键-值对的键。此参数的值应为您要修改的表中某一列的名称。
  •     previousResult:applyBatch() 中 ContentProviderResult 对象数组内某一值以 0 开始的索引。 应用批处理操作时,每个操作的结果都存储在一个中间结果数组内。previousResult 值是其中一个结果的索引,它通过 key 值进行检索和存储。 这样,您就可以插入一条新的原始联系人记录,并取回其 android.provider.BaseColumns#_ID 值,然后在添加 ContactsContract.Data 行时“向后引用”该值。
    系统会在您首次调用 applyBatch() 时创建整个结果数组,其大小与您提供的 ContentProviderOperation 对象的 ArrayList 大小相等。 不过,结果数组中的所有元素都设置为 null,如果您试图向后引用某个尚未应用的操作的结果, withValueBackReference() 会引发一个 Exception。

    以下代码段说明如何批量插入新原始联系人和数据。代码段中包括用于建立屈服点和使用向后引用的代码。 这些代码段是扩展版本的 createContacEntry() 方法,该方法是  Contact Manager 示例应用中 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 operationsops.add(op.build());

    接着,代码会创建显示姓名行、电话行和电子邮件行的数据行。

    每个操作生成器对象都使用 withValueBackReference() 来获取 RAW_CONTACT_ID。引用指回来自第一次操作的 ContentProviderResult 对象,第一次操作就是添加原始联系人行并返回其新 android.provider.BaseColumns#_ID 值。 结果是,每个数据行都通过其 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 operationsops.add(op.build());// Inserts the specified phone number and type as a Phone data rowop = 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 operationsops.add(op.build());// Inserts the specified email and type as a Phone data rowop = 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 operationsops.add(op.build());

    最后一个代码段显示的是 applyBatch() 调用,用于插入新原始联系人行和数据行。

 // Ask the Contacts Provider to create a new contactLog.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 warningContext ctx = getApplicationContext();CharSequence txt = getString(R.string.contactCreationFailure);int duration = Toast.LENGTH_SHORT;Toast toast = Toast.makeText(ctx, txt, duration);toast.show();// Log exceptionLog.e(TAG, "Exception encountered while inserting contact: " + e);}
}

    此外,您还可以利用批处理操作实现乐观并发控制,这是一种无需锁定底层存储库便可应用修改事务的控制方法。 要使用此方法,您需要应用事务,然后检查是否存在可能已同时做出的其他修改。 如果您发现了不一致的修改,请回滚事务并重试。

    乐观并发控制对于移动设备很有用,因为在移动设备上,同一时间只有一位用户,并且同时访问数据存储库的情况很少见。 由于未使用锁定功能,因此不用浪费时间设置锁定或等待其他事务解除锁定。

    要在更新某个 ContactsContract.RawContacts 行时使用乐观并发控制,请按以下步骤操作:

    1. 检索原始联系人的 VERSION 列以及要检索的其他数据。

    2. 创建一个适合使用 newAssertQuery(Uri) 方法强制执行约束 的 ContentProviderOperation.Builder 对象。对于内容 URI,请使用追加有原始联系人 android.provider.BaseColumns#_ID 的 RawContacts.CONTENT_URI 。

    3. 对于 ContentProviderOperation.Builder 对象,请调用 withValue(),对 VERSION 列与您刚检索的版本号进行比较。

    4. 对于同一 ContentProviderOperation.Builder,请调用 withExpectedCount(),确保此断言只对一行进行测试。

    5. 调用 build() 创建 ContentProviderOperation 对象,然后将此对象添加为要传递至 applyBatch() 的 ArrayList 中的第一个对象。

    6. 应用批处理事务。

    如果在您读取原始联系人行到您试图对其进行修改这段时间有另一项操作更新了该行,“断言”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 valuesmRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}
...
// Sets up a Uri for the assert operation
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);
// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);
// Adds the assertions to the assert operation: checks the version and count of rows tested
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);
// Creates an ArrayList to hold the ContentProviderOperation objects
ArrayList 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 thrown
try{ContentProviderResult[] results =getContentResolver().applyBatch(AUTHORITY, ops);} catch (OperationApplicationException e) {// Actions you want to take if the assert operation fails go here}

    通过 Intent 执行检索和修改

    通过向设备的联系人应用发送 Intent,您可以间接访问联系人提供程序。 Intent 会启动设备的联系人应用 UI,用户可以在其中执行与联系人有关的操作。 通过这种访问方式,用户可以:

    •从列表中选取一位联系人并将其返回给您的应用以执行进一步操作。

    •编辑现有联系人的数据。

    •为其任一帐户插入新原始联系人。

    •删除联系人或联系人数据。

    如果用户要插入或更新数据,您可以先收集数据,然后将其作为 Intent 的一部分发送。

    当您使用 Intent 通过设备的联系人应用访问联系人提供程序时,您无需自行编写用于访问该提供程序的 UI 或代码。 您也无需请求对提供程序的读取或写入权限。 设备的联系人应用可以将联系人读取权限授予给您,而且您是通过另一个应用对该提供程序进行修改,不需要拥有写入权限。

    内容提供程序基础知识指南“通过 Intent 访问数据”部分详细描述了通过发送 Intent 来访问某提供程序的一般过程。 下表(联系人提供程序 Intent)汇总了您为可用任务使用的操作、MIME 类型以及数据值,ContactsContract.Intents.Insert 参考文档列出了您可用于putExtra() 的 Extra 值:

7

    设备的联系人应用不允许您使用 Intent 删除原始联系人或其任何数据。 因此,要删除原始联系人,请使用 ContentResolver.delete() 或 ContentProviderOperation.newDelete()。

    以下代码段说明如何构建和发送一个插入新原始联系人和数据的 Intent:

// Gets values from the UI
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();
//
// Creates a new intent for sending to the device's contacts application
Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);
//
// Sets the MIME type to the one expected by the insertion activity
insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
//
// Sets the new contact name
insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);
//
// Sets the new company and job title
insertIntent.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 row
ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();
//
//
/** Defines the raw contact row*/
//
// Sets up the row as a ContentValues object
ContentValues rawContactRow = new ContentValues();
//
// Adds the account type and name to the row
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
//
// Adds the row to the array
contactData.add(rawContactRow);
//
/** Sets up the phone number data row*/
//
// Sets up the row as a ContentValues object
ContentValues 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 row
phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);
//
// Adds the row to the array
contactData.add(phoneRow);
//
/** Sets up the email data row*/
//
// Sets up the row as a ContentValues object
ContentValues 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 row
emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);
//
// Adds the row to the array
contactData.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.RawContacts 行添加一个 ContactsContract.CommonDataKinds.StructuredName行。如果 ContactsContract.Data 表中的 ContactsContract.RawContacts 行没有 ContactsContract.CommonDataKinds.StructuredName 行,可能会在聚合时引发问题。务必将新 ContactsContract.Data 行链接到其父 ContactsContract.RawContacts 行。如果 ContactsContract.Data 行未链接到 ContactsContract.RawContacts,则其在设备的联系人应用中将处于不可见状态,而且这可能会导致同步适配器出现问题。请仅更改您拥有的那些原始联系人的数据。请切记,联系人提供程序所管理的数据通常来自多个不同帐户类型/在线服务。您需要确保您的应用仅修改或删除归您所有的行的数据,并且仅通过您控制的帐户类型和帐户名称插入数据。务必使用在 ContactsContract 及其子类中为权限、内容 URI、URI 路径、列名称、MIME 类型以及 TYPE 值定义的常量。使用这些常量有助于您避免错误。如有任何常量被弃用,您还会从编译器警告收到通知。

    自定义数据行

    通过创建和使用自己的自定义 MIME 类型,您可以在 ContactsContract.Data 表中插入、编辑、删除和检索您的自有数据行。 这些行仅限使用 ContactsContract.DataColumns 中定义的列,但您可以将您自己的类型专用列名称映射到默认列名称。 在设备的联系人应用中,会显示这些行的数据,但无法对其进行编辑或删除,用户也无法添加其他数据。要允许用户修改您的自定义数据行,您必须在自己的应用中提供编辑器 Activity。要显示您的自定义数据,请提供一个 contacts.xml 文件,其中须包含一个 <ContactsAccountType> 元素,及其一个或多个 <ContactsDataKind> 子元素。<ContactsDataKind> element 部分对此做了更详尽的描述。如需了解有关自定义 MIME 类型的更多信息,请阅读创建内容提供程序指南。

联系人提供程序同步适配器

    联系人提供程序专门设计用于处理设备与在线服务之间的联系人数据同步。 借助同步功能,用户可以将现有数据下载到新设备,以及将现有数据上传到新帐户。 此外,同步还能确保用户掌握最新数据,无需考虑数据增加和更改的来源。 同步的另一个优点是,即使设备未连接网络,联系人数据同样可用。虽然您可以通过各种方式实现同步,不过 Android 系统提供了一个插件同步框架,可自动化完成下列任务:•检查网络可用性。• 根据用户偏好安排和执行同步。• 重启已停止的同步。要使用此框架,您需要提供一个同步适配器插件。每个同步适配器都专用于某个服务和内容提供程序,但可以处理同一服务的多个帐户名称。 该框架还允许同一服务和提供程序具有多个同步适配器。 

    同步适配器类和文件

    您需要将同步适配器作为 AbstractThreadedSyncAdapter 的子类进行实现,并作为 Android 应用的一部分进行安装。系统通过您的应用清单文件中的元素以及由清单文件指向的一个特殊 XML 文件了解有关同步适配器的信息。 该 XML 文件定义在线服务的帐户类型和内容提供程序的权限,它们共同对适配器进行唯一标识。 用户为同步适配器的帐户类型添加一个帐户,并为与同步适配器同步的内容提供程序启用同步后,同步适配器才会激活。激活后,系统将开始管理适配器,并在必要时调用它,以在内容提供程序与服务器之间同步数据。注:将帐户类型用作同步适配器标识的一部分让系统可以发现从同一组织访问不同服务的同步适配器,并将它们组合在一起。 例如,Google 在线服务的同步适配器都具有相同的帐户类型 com.google。 当用户向其设备添加 Google 帐户时,已安装的所有 Google 服务同步适配器将一起列出;列出的每个同步适配器都与设备上不同的内容提供程序同步。大多数服务都要求用户验证身份后才能访问数据,为此,Android 系统提供了一个身份验证框架,该框架与同步适配器框架类似,并且经常与其联用。该身份验证框架使用的插件身份验证器是 AbstractAccountAuthenticator 的子类。 身份验证器通过下列步骤验证用户的身份:1. 收集用户名、用户密码或类似信息(用户的凭据)。2. 将凭据发送给服务3. 检查服务的回复。如果服务接受了凭据,身份验证器便可存储凭据以供日后使用。 由于插件身份验证器框架的存在,AccountManager 可以提供对身份验证器支持并选择公开的任何身份验证令牌(例如 OAuth2 身份验证令牌)的访问。尽管身份验证并非必需,但大多数联系人服务都会使用它。 不过,您不一定要使用Android 身份验证框架进行身份验证。

    同步适配器实现

    要为联系人提供程序实现同步适配器,您首先要创建一个包含以下内容的 Android 应用:一个 Service 组件,用于响应系统发出的绑定到同步适配器的请求。当系统想要运行同步时,它会调用服务的 onBind() 方法,为同步适配器获取一个 IBinder。这样,系统便可跨进程调用适配器的方法。在示例同步适配器示例应用中,该服务的类名是 com.example.android.samplesync.syncadapter.SyncService。作为AbstractThreadedSyncAdapter 具体子类实现的实际同步适配器。此类的作用是从服务器下载数据、从设备上传数据以及解决冲突。 适配器的主要工作是在方法 onPerformSync() 中完成的。 必须将此类实例化为单一实例。在示例同步适配器示例应用中,同步适配器是在 com.example.android.samplesync.syncadapter.SyncAdapter 类中定义的。Application 的子类。此类充当同步适配器单一实例的工厂。使用 onCreate() 方法实例化同步适配器,并提供一个静态“getter”方法,使单一实例返回同步适配器服务的onBind() 方法。可选:一个 Service 组件,用于响应系统发出的用户身份验证请求。AccountManager 会启动此服务以开始身份验证流程。 该服务的 onCreate() 方法会将一个身份验证器对象实例化。 当系统想要对应用同步适配器的用户帐户进行身份验证时,它会调用该服务的 onBind() 方法,为该身份验证器获取一个 IBinder。 这样,系统便可跨进程调用身份验证器的方法。在示例同步适配器示例应用中,该服务的类名是 com.example.android.samplesync.authenticator.AuthenticationService。可选:一个用于处理身份验证请求的AbstractAccountAuthenticator 具体子类。AccountManager 就是调用此类所提供的方法向服务器验证用户的凭据。 详细的身份验证过程会因服务器所采用技术的不同而有很大差异。 您应该参阅服务器软件的文档,了解有关身份验证的更多信息。在示例同步适配器示例应用中,身份验证器是在 com.example.android.samplesync.authenticator.Authenticator 类中定义的。用于定义系统同步适配器和身份验证器的 XML 文件。之前描述的同步适配器和身份验证器服务组件都是在应用清单文件中的<service> 元素内定义的。 这些元素包含以下用于向系统提供特定数据的 <meta-data> 子元素: • 同步适配器服务的 <meta-data> 元素指向 XML 文件 res/xml/syncadapter.xml。而该文件则指定将与联系人提供程序同步的 Web 服务的 URI,以及指定该 Web 服务的帐户类型。• 可选:身份验证器的 <meta-data> 元素指向 XML 文件 res/xml/authenticator.xml。而该文件则指定此身份验证器所支持的帐户类型,以及指定身份验证过程中出现的UI 资源。 在此元素中指定的帐户类型必须与为同步适配器指定的帐户类型相同。

    社交流数据

    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#COMMENTS可选。存储可在流项目开头显示的摘要信息。android.provider.ContactsContract.StreamItemsColumns#TEXT流项目的文本,或为项目来源发布的内容,或是对生成流项目的某项操作的描述。 此列可包含可由 fromHtml() 渲染的任何格式设置和嵌入式资源图像。 提供程序可能会截断或省略较长内容,但它会尽力避免破坏标记。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#SYNC1 至 android.provider.ContactsContract.StreamItemsColumns#SYNC4。

    社交流照片

android.provider.ContactsContract.StreamItemPhotos 表存储与流项目关联的照片。 该表的 android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID列链接到 android.provider.ContactsContract.StreamItems表 android.provider.BaseColumns#_ID 列中的值。 照片引用存储在表中的以下列:android.provider.ContactsContract.StreamItemPhotos#PHOTO 列(一个二进制大型对象)。照片的二进制表示,为便于存储和显示,由提供程序调整了尺寸。 此列可用于向后兼容使用它来存储照片的旧版本联系人提供程序。 不过,在当前版本中,您不应使用此列来存储照片, 而应使用 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一个内容 URI,直接指向此行所表示的照片的照片文件。 通过此 URI 调用 openAssetFileDescriptor() 以获得照片文件的句柄。

    使用社交流表

这些表的工作方式与联系人提供程序中的其他主表基本相同,不同的是:• 这些表需要额外的访问权限。要读取它们的数据,您的应用必须具有 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。 查询会返回一个 Cursor,其中包含一行,并且只有 android.provider.ContactsContract.StreamItems#MAX_ITEMS 一列。android.provider.ContactsContract.StreamItems.StreamItemPhotos 类定义了 android.provider.ContactsContract.StreamItemPhotos 的一个子表,其中包含某个流项目的照片行。

    社交流交互

    通过将联系人提供程序管理的社交流数据与设备的联系人应用相结合,可以在您的社交网络系统与现有联系人之间建立起有效的连接。 这种结合实现了下列功能: • 您可以通过同步适配器让您的社交网络服务与联系人提供程序同步,检索用户联系人的近期 Activity,并将其存储在 android.provider.ContactsContract.StreamItems 表和android.provider.ContactsContract.StreamItemPhotos 表中,以供日后使用。 • 除了定期同步外,您还可以在用户选择某位联系人进行查看时触发您的同步适配器以检索更多数据。 这样,您的同步适配器便可检索该联系人的高分辨率照片和最近流项目。 • 通过在设备的联系人应用以及联系人提供程序中注册通知功能,您可以在用户查看联系人时收到一个 Intent,并在那时通过您的服务更新联系人的状态。 与通过同步适配器执行完全同步相比,此方法可能更快速,占用的带宽也更少。 • 用户可以在查看设备联系人应用中的联系人时,将其添加到您的社交网络服务。您可以通过“邀请联系人”功能实现此目的,而该功能则是通过将 Activity 与 XML 文件结合使用来实现的,前者将现有联系人添加到您的社交网络,后者为设备的联系人应用以及联系人提供程序提供有关您的应用的详细信息。 流项目与联系人提供程序的定期同步与其他同步相同。 如需了解有关同步的更多信息,请参阅 联系人提供程序同步适配器部分。接下来的两节介绍如何注册通知和邀请联系人。 通过注册处理社交网络查看 要注册您的同步适配器,以便在用户查看由您的同步适配器管理的联系人时收到通知,请执行以下步骤: 1. 在您项目的res/xml/ 目录中创建一个名为 contacts.xml 的文件。 如果您已有该文件,可跳过此步骤。 2. 在该文件中添加元素 <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">。 如果该元素已存在,可跳过此步骤。 3. 要注册一项服务,以便在用户于设备的联系人应用中打开某位联系人的详细信息页面时通知该服务,请为该元素添加 viewContactNotifyService="serviceclass"属性,其中 serviceclass 是该服务的完全限定类名,应由该服务接收来自设备联系人应用的 Intent。 对于这个通知程序服务,请使用一个扩展 IntentService 的类,以让该服务能够接收 Intent。 传入 Intent 中的数据包含用户点击的原始联系人的内容 URI。 您可以通过通知程序服务绑定到您的同步适配器,然后调用同步适配器来更新原始联系人的数据。 要注册需要在用户点击流项目或照片(或同时点击这两者)时调用的 Activity,请执行以下步骤: 1. 在您项目的 res/xml/目录中创建一个名为 contacts.xml 的文件。 如果您已有该文件,可跳过此步骤。 2. 在该文件中添加元素 <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">。 如果该元素已存在,可跳过此步骤。 3. 要注册某个 Activity,以处理用户在设备联系人应用中点击某个流项目的操作,请为该元素添加 viewStreamItemActivity="activityclass" 属性,其中 activityclass是该 Activity 的完全限定类名,应由该 Activity 接收来自设备联系人应用的 Intent。 4. 要注册某个 Activity,以处理用户在设备联系人应用中点击某个流照片的操作,请为该元素添加 viewStreamItemPhotoActivity="activityclass" 属性,其中 activityclass 是该 Activity 的完全限定类名,应由该 Activity 接收来自设备联系人应用的 Intent。 <ContactsAccountType> 元素部分对 <ContactsAccountType>元素做了更详尽的描述。 传入 Intent 包含用户点击的项目或照片的内容 URI。 要让文本项目和照片具有独立的 Activity,请在同一文件中使用这两个属性。 与您的社交网络服务交互 用户不必为了邀请联系人到您的社交网络网站而离开设备的联系人应用。 取而代之是,您可以让设备的联系人应用发送一个 Intent,将联系人 邀请到您的 Activity 之一。要设置此功能,请执行以下步骤: 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 元素,这些元素控制您的同步适配器和应用与联系人应用及联系人提供程序的交互。 下文对这些元素做了描述。 <ContactsAccountType> 元素 <ContactsAccountType> 元素控制您的应用与联系人应用的交互。 它采用了以下语法:

<ContactsAccountTypexmlns: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 Add connection 菜单中为 inviteContactActivity 中指定的 Activity 显示的文本字符串。 例如,您可以使用字符串“Follow in my network”。您可以为此标签使用字符串资源标识符。 viewContactNotifyService您的应用中某项服务的完全限定类名,当用户查看联系人时,应由该服务接收通知。 此通知由设备的联系人应用发送;您的应用可以根据通知将数据密集型操作推迟到必要时再执行。 例如,您的应用对此通知的响应可以是:读入并显示联系人的高分辨率照片和最近的社交流项目。 社交流交互部分对此功能做了更详尽的描述。 您可以在 SampleSyncAdapter 示例应用的 NotifierService.java 文件中查看通知服务的示例。 viewGroupActivity 您的应用中某个可显示组信息的 Activity 的完全限定类名。当用户点击设备联系人应用中的组标签时,将显示此 Activity 的 UI。 viewGroupActionLabel 联系人应用为某个 UI 控件显示的标签,用户可通过该控件查看您的应用中的组。 例如,如果您在设备上安装了 Google+ 应用,并将 Google+ 与联系人应用同步,就会看到 Google+ 圈子以组的形式出现在您的联系人应用的 Groups 选项卡内。 如果您点击某个 Google+ 圈子,就会看到该圈子内的联系人以“组”的形式列出。在该显示页面的顶部,您会看到一个 Google+ 图标;如果您点击它,控制权将切换给Google+ 应用。联系人应用以 Google+ 图标作为 viewGroupActionLabel 的值,通过 viewGroupActivity 来实现此目的。 允许使用字符串资源标识符作为该属性的值。 viewStreamItemActivity 您的应用中某个 Activity 的完全限定类名,设备的联系人应用会在用户点击原始联系人的流项目时启动该 Activity。 viewStreamItemPhotoActivity 您的应用中某个 Activity 的完全限定类名,设备的联系人应用会在用户点击原始联系人流项目中的照片时启动该Activity。 <ContactsDataKind> 元素 <ContactsDataKind> 元素控制您的应用的自定义数据行在联系人应用 UI 中的显示。它采用了以下语法: <ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name"> 包含它的文件: <ContactsAccountType>描述: 此元素用于让联系人应用将自定义数据行的内容显示为原始联系人详细信息的一部分。 <ContactsAccountType> 的每个 <ContactsDataKind> 子元素都代表您的同步适配器向 ContactsContract.Data 表添加的某个自定义数据行类型。 请为您使用的每个自定义 MIME 类型添加一个 <ContactsDataKind> 元素。 如果您不想显示任何自定义数据行的数据,则无需添加该元素。 属性: android:mimeType 您为 ContactsContract.Data表中某个自定义数据行类型定义的自定义 MIME 类型。例如,可将值 vnd.android.cursor.item/vnd.example.locationstatus 作为记录联系人最后已知位置的数据行的自定义 MIME 类型。 android:icon 联系人应用在您的数据旁显示的 Android Drawable资源。 它用于向用户指示数据来自您的服务。 android:summaryColumn 从数据行检索的两个值中第一个值的列名。该值显示为该数据行的第一个输入行。 第一行专用作数据摘要,不过它是可选项。另请参阅 android:detailColumn。 android:detailColumn 从数据行检索的两个值中第二个值的列名。该值显示为该数据行的第二个输入行。 另请参阅 android:summaryColumn。

其他联系人提供程序功能

    除了上文描述的主要功能外,联系人提供程序还为处理联系人数据提供了下列有用的功能:•联系人组•照片功能

    联系人组

联系人提供程序可以选择性地为相关联系人集合添加组数据标签。 如果与某个用户帐户关联的服务器想要维护组,则与该帐户的帐户类型对应的同步适配器应在联系人提供程序与服务器之间传送组数据。 当用户向服务器添加一个新联系人,然后将该联系人放入一个新组时,同步适配器必须将这个新组添加到 ContactsContract.Groups 表中。 原始联系人所属的一个或多个组使用ContactsContract.CommonDataKinds.GroupMembership MIME 类型存储在 ContactsContract.Data 表内。 如果您设计的同步适配器会将服务器中的原始联系人数据添加到联系人提供程序,并且您不使用组,则需要指示提供程序让您的数据可见。 在用户向设备添加帐户时执行的代码中,更新联系人提供程序为该帐户添加的 ContactsContract.Settings 行。 在该行中,将 Settings.UNGROUPED_VISIBLE 列的值设置为 1。执行此操作后,即使您不使用组,联系人提供程序也会让您的联系人数据始终可见。

    联系人照片

    ContactsContract.Data 表通过 MIME 类型 Photo.CONTENT_ITEM_TYPE 以行的形式存储照片。该行的 CONTACT_ID 列链接到其所属原始联系人的 android.provider.BaseColumns#_ID 列。 ContactsContract.Contacts.Photo 类定义了一个 ContactsContract.Contacts子表,其中包含联系人主要照片(联系人的主要原始联系人的主要照片)的照片信息。 同样, ContactsContract.RawContacts.DisplayPhoto 类定义了一个 ContactsContract.RawContacts 子表,其中包含原始联系人主要照片的照片信息。 ContactsContract.Contacts.Photo 和 ContactsContract.RawContacts.DisplayPhoto 参考文档包含检索照片信息的示例。 并没有可用来检索原始联系人主要缩略图的实用类,但您可以向ContactsContract.Data 表发送查询,从而通过选定原始联系人的 android.provider.BaseColumns#_ID、 Photo.CONTENT_ITEM_TYPE 以及 IS_PRIMARY 列,找到原始联系人的主要照片行。 联系人的社交流数据也可能包含照片。这些照片存储在 android.provider.ContactsContract.StreamItemPhotos 表中,社交流照片部分对该表做了更详尽的描述。

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Web页面分页的几种方式

    JavaWeb分页显示内容之分页查询的三种思路(数据库分页查询)转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6134851.html在开发过程中,经常做的一件事,也是最基本的事,就是从数据库中查询数据,然后在客户端显示出来。当数据少时,可以在一个页面内显示完成。然而,…...

    2024/4/23 18:56:34
  2. 写点啥呢

    写点啥呢,这太让我郁闷了,写点工作上的事吧,工作内容又都是保密的,不允许写;写点生活中的事吧,琐事不爱写,感情上的事又不敢写,万一哪天让老婆看见了,还不得要了我的命,郁闷啊,郁闷![@more@]工来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/7253474/viewspa…...

    2024/4/18 1:13:30
  3. 阿里巴巴技术笔试心得《技术之瞳》例题错误答案汇总(持续更新)

    1.《技术之瞳》简介《技术之瞳——阿里巴巴技术笔试心得》由阿里巴巴集团校园招聘笔试项目组所著,收集了阿里历年校招中的精华笔试题,涉 及多个领域。《技术之瞳——阿里巴巴技术笔试心得》中内容大量结合了阿里巴巴的实际工作场景,以例题、解析、习题的形式,引 导读者深入…...

    2024/4/9 11:12:37
  4. HTML分页插件功能实现

    分页功能实现案例展示如下: 1.先引入css样式和jQuery,再引入分页插件toPage.js,谨记顺序不能乱 <link rel="stylesheet" href="css/toPage.css"> <script src="script/jquery.min.js"></script> <link rel="styles…...

    2024/4/12 16:30:47
  5. idea 注册码 20190326

    亲测可用N757JE0KCT-eyJsaWNlbnNlSWQiOiJONzU3SkUwS0NUIiwibGljZW5zZWVOYW1lIjoid3UgYW5qdW4iLCJhc3NpZ25lZU5hbWUiOiIiLCJhc3NpZ25lZUVtYWlsIjoiIiwibGljZW5zZVJlc3RyaWN0aW9uIjoiRm9yIGVkdWNhdGlvbmFsIHVzZSBvbmx5IiwiY2hlY2tDb25jdXJyZW50VXNlIjpmYWxzZSwicHJvZHVjdHMiOlt7I…...

    2024/4/12 16:30:52
  6. 边缘橙子的JavaWeb学习记录和感悟

    JavaWeb学习记录_01 之前在网上看过韩顺平老师讲解tomcat的视频,很细致,我也跟着视频做了些笔记,印象最深的就是他把tomcat安装目录下的每个文件夹的用途都梳理了一遍。缘是,今天重新拾起jsp的学习,教程一开始的关于tomcat的一些内容接受的也就很快了。 简单回顾下今天对j…...

    2024/4/20 13:56:43
  7. 《STL源码剖析》

    1、容器(containers) 提供各种数据结构,例如 vector、list、deque、set、map 等主要用于存放各类数据。因此称之为 容器,是一种 class template 2、算法(algorithm) 算法包括 sort 、search、copy、erase 等常见算法,是一种function template 3、迭代器(iterators) 迭代器是…...

    2024/4/12 16:30:37
  8. vim设置参数以方便YAML语句录入

    2019独角兽企业重金招聘Python工程师标准>>> Ansible的剧本等文件都采用YAML一样的对齐方式,一般同等级别的代码行会对齐,直接的下级,则会后退两个空格。退错一个空格就会出错!有时,正在编辑的这一行前面了6格,紧接着要输入下一行。一回车,光标跑到下一行最前…...

    2024/4/12 16:30:52
  9. 一道阿里巴巴笔试题中,关于java中的变量,初始化快,构造函数初始化分析

    本人菜鸟一只,断断续续学习java一年有余,之前一只都是转载各位大神的帖子,不敢献丑。前几天找工作遇到了一道笔试试题,让我困惑许久。查了一些资料,加上一些自己的理解,终于算是大体上明白了,也下决心写下自己的第一篇技术类博客(由于本人实在技术有限,如有错误,还请…...

    2024/4/12 16:30:57
  10. 阿里巴巴一道笔试题

    其实这是谷歌的一道面试题::给定能随机生成整数1到5的函数,写出能随机生成整数1到7的函数 惭愧,阿里的笔试题做错了,说实话,以前没见过,见过了就把搞懂 阿里题目: 给定rand_7()表示能生成1-7的随机数,使用四则运算和循环等控制语句,可以输出的有() A.rand_3() B…...

    2024/4/14 4:00:49
  11. ElasticSearch查询—分页查询详解

    Elasticsearch中数据都存储在分片中,当执行搜索时每个分片独立搜索后,数据再经过整合返回。那么,如何实现分页查询呢?按照一般的查询流程来说,如果我想查询前10条数据:1)客户端请求发给某个节点2)节点转发给个个分片,查询每个分片上的前10条3)结果返回给节点,整合数据,…...

    2024/4/9 11:12:29
  12. Oracle基础:4: sql执行错误时的控制方式

    多条sql执行时如果在中间的语句出现错误,后续会不会直接执行,如何进行设定,以及其他数据库诸如Mysql是如何对应的,这篇文章将会进行简单的整理和说明。 环境准备 使用Oracle的精简版创建docker方式的demo环境,详细可参看:https://blog.csdn.net/liumiaocn/article/detail…...

    2024/4/19 18:03:35
  13. Linux IDEA 下载、解压、使用

    总目录 - Ubuntu 应用软件安装 && 开发环境搭建测试 目录下载 wget https://download.jetbrains.8686c.com/idea/ideaIC-2019.3.2.tar.gz 更多版本见IDEA 官网下载页 解压 sudo tar -zxvf ideaIC-2019.3.2.tar.gz -C /opt 运行 /opt/idea-IC-193.6015.39/bin/idea.sh …...

    2024/4/18 7:35:46
  14. STL源码剖析----算法、仿函数、配接器

    一、STL中的算法 1. sort 这个算法接收两个随机存取迭代器,所以vector、deque可以用。而list迭代器属于只双向迭代器。数据量大时采用QuickSort,分段递归排序,一旦分段后的数据量小于某个门槛,则采用insertSort。SGI STL用的introSort来替代常规的QuickSort,这两者的区别就…...

    2024/4/20 14:01:52
  15. FTP文件 BCP入库

    因为BCP导入性能要比其他的第三方工具kettle等强很多,所以优先应考虑使用BCP IN 的方式导入接口方传过来的文本文件,遇到的主要问题如下1.文件格式问题文件格式通常有三种 unix,dos,mac三种,可以在脚本中分别用dos2unix/mac2unix进行转换 2.文件编码问题通过iconv进行转换…...

    2024/4/17 21:32:19
  16. 阿里2017笔试(3)

    只需要最基本的同余即可。 7^4=2401≡1(mod 100) 2014%4=2 所以7^2014≡7^2=49(mod 100) 所以十位是42L和3L都是非常容易获取的。 只要能获得2L和3L,那么除了1之外的任何正整数,都是可以获取的了。 例如6=2+2+2,8=2+2+2+2,11=3+2+2+2+2...

    2024/4/20 6:10:31
  17. 分页和非分页内存

    windows规定有些虚拟内存页面是可以交换到文件中的,这类内存被称为分页内存。还有一些虚拟内存是永远不会交换到文件中,这类内存被称为非分页内存。当程序的中断请求级别在DISPATCH_LEVEL之上的时候(包括DISPATCH_LEVEL层)。程序只能使用非分页内存,否则会导致蓝屏 我们可…...

    2024/4/12 16:30:42
  18. 《STL源码剖析》——迭代器(iterators)概念与traits编程技法(二)

    一、SGI STL 的私房菜:__type_traits__type_traits 负责萃取型别(type)的特性。此处所关注的型别特性是指:这个型别是否具备 non-trivial defalt ctor?是否具备 non-trivial copy ctor?是否具备 non-trivial assignment operator?是否具备 non-trivial dtor?如果答案是…...

    2024/4/20 8:04:41
  19. IDEA Intellij EAP 11的几个牛逼闪闪的新功能

    我是idea的big fan,在twitter关注了idea的微博,看到提到过不少新功能,于是尝试了最新的110.291这个版本,包含了最新的功能,试用了一下,有三个很值得分享的功能.1.类名的模糊匹配当你写错类名,然后按下ctrl+空格的时候,你会惊喜地发现,仍然可以获得动态提示,如:输入hasmap,少打了…...

    2024/4/18 2:00:48
  20. 如何把JavaWeb项目部署到阿里云服务器并用公网ip访问

    最近在学习Linux系统,所以试着部署了一下之前做过的javaweb项目,简单说一下主要步骤:1.到阿里云官网上购买一个云服务器2.在你的电脑里下载安装一个Xshell5,连接你的云服务器3.远程向你的服务器安装JDK、Tomcat以及Mysql你可能用到的教程(这些教程都是从大神们的博客里搜来…...

    2024/4/9 11:12:23

最新文章

  1. shell脚本使用小技巧-持续更新

    1.假如下面的shell脚本"2024_extractrG4_calculating_score_filternonoverlapG4.sh" #!/bin/bash python lin_change_gff3original.py -i iwgsc_refseqv2.1_annotation_200916_HC_LC.gff3 -o iwgsc_refseqv2.1_annotation_200916_HC_LC.gff4# Define base filenames…...

    2024/4/30 16:34:08
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. LeetCode 热题 100 题解(二):双指针部分(2)| 滑动窗口部分(1)

    题目四&#xff1a;接雨水&#xff08;No. 43&#xff09; 题目链接&#xff1a;https://leetcode.cn/problems/trapping-rain-water/description/?envTypestudy-plan-v2&envIdtop-100-liked 难度&#xff1a;困难 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&am…...

    2024/4/30 0:27:46
  4. JRT高效率开发

    得益于前期的基础投入&#xff0c;借助代码生成的加持&#xff0c;本来计划用一周实现质控物维护界面&#xff0c;实际用来四小时左右完成质控物维护主体&#xff0c;效率大大超过预期。 JRT从设计之初就是为了证明Spring打包模式不适合软件服务模式&#xff0c;觉得Spring打包…...

    2024/4/30 3:28:17
  5. windows更新驱动导致Linux虚拟机网卡找不到

    windows更新驱动导致Linux虚拟机网卡找不到 1、现象2、解决过程3、参考 1、现象 原先虚拟机配置了静态IP&#xff0c;更新windows驱动后xshell连接不上这台虚拟机&#xff08;其他几台也是&#xff09;。 2、解决过程 service network restart出现一下报错&#xff1a; Rest…...

    2024/4/30 4:13:40
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/29 23:16:47
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/29 6:03:24
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/29 14:21:50
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/30 9:43:22
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57