聯絡人供應程式

聯絡人提供者是功能強大且靈活的 Android 元件,可管理 裝置上使用者相關資料的中央存放區。聯絡人供應程式是資料來源 您在裝置「聯絡人」應用程式中看到的資料,您也可以存取自己的資料 應用程式,並在裝置與線上服務之間移轉資料。供應商可配合 並嘗試盡可能管理每位使用者的資料 機構也相當複雜因此,供應商的 API 包含 提供豐富多樣的合約類別與介面,有助於資料擷取與 修改內容。

本指南將說明以下內容:

  • 基本的供應器結構。
  • 如何從供應器擷取資料。
  • 如何修改供應器中的資料。
  • 如何編寫同步轉換介面,以將伺服器中的資料同步到 聯絡人供應程式。

本指南假設您瞭解 Android 內容供應器的基本概念。瞭解詳情 如要進一步瞭解 Android 內容供應器,請詳閱 內容供應器基礎知識指南。

聯絡人提供者機構

聯絡人供應程式是 Android 內容供應器元件,它會維護三種類型的個人資料,每個資料都對應至供應商提供的資料表,如圖 1 所示:

圖 1. 聯絡人提供者表格結構。

這三個表格通常以合約類別的名稱來指稱。課程 定義資料表使用的內容 URI、資料欄名稱和資料欄值等常數:

ContactsContract.Contacts 表格
根據原始聯絡人資料列的匯總資料,列出代表不同人的資料列。
ContactsContract.RawContacts 個資料表
包含使用者帳戶和類型專屬使用者資料摘要的資料列。
ContactsContract.Data 個資料表
列包含原始聯絡人的詳細資料,例如電子郵件地址或電話號碼。

ContactsContract 中由合約類別代表的其他資料表 是「聯絡人供應程式」用來管理其營運或支援服務的輔助表格 裝置聯絡人或電話應用程式中的特定功能。

原始聯絡人

「原始聯絡人」代表使用者的資料來自單一帳戶類型和帳戶 名稱。因為聯絡人供應程式允許多個線上服務 單一聯絡人的資料,則聯絡人供應程式允許同一使用者有多個原始聯絡人。 建立多個原始聯絡人也能讓使用者將多個帳戶的資料合併在一起 同類型帳戶。

原始聯絡人的大部分資料並未儲存在 ContactsContract.RawContacts 資料表中。而是儲存在 ContactsContract.Data 資料表的一或多個資料列中。每個資料列都有一個資料欄 Data.RAW_CONTACT_ID 符合 包含其 RawContacts._ID 值 父項 ContactsContract.RawContacts 資料列。

重要原始聯絡人欄

ContactsContract.RawContacts 資料表中的重要欄位列於表 1 中。請參閱表格後方的附註:

表 1. 重要原始聯絡人欄。

資料欄名稱 使用 附註
ACCOUNT_NAME 此原始聯絡人來源的帳戶類型。 舉例來說,Google 帳戶的名稱是裝置擁有者的 Gmail 帳戶名稱 讓我們看看 DNS 解析 進一步探索內部和外部位址請參閱下項 ACCOUNT_TYPE: 可能不準確或不適當 這個名稱的格式會因帳戶類型而異。不 不必輸入電子郵件地址
ACCOUNT_TYPE 這個原始聯絡人的來源帳戶類型。例如 屬於com.google的 Google 帳戶類型。請務必使用您擁有或控管的網域識別碼,確認帳戶類型。這樣就能確保帳戶類型不重複。 提供聯絡人資料的帳戶類型通常會有相關聯的同步轉換介面, 會與聯絡人供應程式同步處理。
DELETED 「已刪除」做為原始聯絡人的標記 這個標記可讓聯絡人提供者於內部保留該資料列,直到同步為止 轉接程式能從伺服器上刪除該資料列,最後刪除該資料列 從存放區複製物件

附註

以下是 ContactsContract.RawContacts 資料表的重要注意事項:

  • 原始聯絡人的名稱不會儲存在 ContactsContract.RawContacts 的資料列中。而是儲存在 ContactsContract.Data 資料表的 ContactsContract.CommonDataKinds.StructuredName 資料列中。原始聯絡人在 ContactsContract.Data 資料表中只有一列。
  • 注意:如要在原始聯絡人資料列中使用您自己的帳戶資料,則必須 必須先向 AccountManager 註冊。為此,請提示使用者將帳戶類型和帳戶名稱加入帳戶清單。如果您不 執行這個動作時,聯絡人供應程式會自動刪除您的原始聯絡列。

    舉例來說,如果您希望應用程式為網頁服務 (網域為 com.example.dataservice) 維護聯絡人資料,而服務的使用者帳戶為 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」

這位使用者已在「帳戶」設定中,為這三個帳戶啟用「同步處理聯絡人」功能。

假設 Emily Dickinson 開啟瀏覽器視窗,以 emily.dickinson@gmail.com 身分登入 Gmail,開啟聯絡人並新增「Thomas Higginson」。之後,她以 emilyd@gmail.com 身分登入 Gmail,並傳送電子郵件給「Thomas Higginson」,系統會自動將他新增為聯絡人。此外,她也追蹤了「corel_tom」(Thomas Higginson 的 Twitter ID) Twitter。

聯絡人供應器會根據這項工作建立三個原始聯絡人:

  1. emily.dickinson@gmail.com 相關聯的「Thomas Higginson」原始聯絡人。使用者帳戶類型為 Google。
  2. 第二次原始接觸「Thomas Higginson」與 emilyd@gmail.com 相關聯。 使用者帳戶類型也是 Google。即使名稱與先前的名稱相同,仍會有第二個原始聯絡人,因為該聯絡人是為了不同的使用者帳戶而新增。
  3. 《Thomas Higginson》的第三位原始聯絡人與「belle_of_amherst」相關聯。使用者帳戶類型為 Twitter。

資料

如先前所述,原始聯絡人的資料會儲存在與原始聯絡人的 _ID 值連結的 ContactsContract.Data 資料列中。如此一來,單一原始聯絡人就能有多個 類型的資料,例如電子郵件地址或電話號碼。舉例來說,如果 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 資料欄旗標 含有該類型主要資料的資料列。舉例來說 使用者長按聯絡人的電話號碼,然後選取 [設定預設值]。 那麼包含該數字的 ContactsContract.Data 列 已經將 IS_PRIMARY 欄設為 非零的值。

一般資料欄名稱

有 15 個一般資料欄,名為 DATA1DATA15 正式發布,並額外提供四項一般性 應僅用於同步處理的 SYNC1SYNC4 欄 轉接器。無論資料列包含哪種資料類型,泛用的資料欄名稱常數一律有效。

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 類型,就可以免費 即可定義自己的類型專屬欄名,並視需求使用欄位。

圖 2 說明描述性資料欄和資料欄如何出現在 「ContactsContract.Data」資料列,以及特定類型的特定資料欄名稱「疊加」如何 一般資料欄名稱

如何將類型特定的資料欄名稱對應至一般資料欄名稱

圖 2. 類型專屬的資料欄名稱和一般資料欄名稱。

類型特定的資料欄名稱類別

表 2 列出最常用的類型特定欄名稱類別:

表 2. 類型專屬的資料欄名稱類別

對應類別 資料類型 附註
ContactsContract.CommonDataKinds.StructuredName 與這個資料列相關聯的原始聯絡人的名稱資料。 原始聯絡人只有這些資料列。
ContactsContract.CommonDataKinds.Photo 與此資料列相關聯的原始聯絡人主要相片。 原始聯絡人只有一個這類資料列。
ContactsContract.CommonDataKinds.Email 與這個資料列相關聯的原始聯絡人電子郵件地址。 原始聯絡人可擁有多個電子郵件地址。
ContactsContract.CommonDataKinds.StructuredPostal 與這個資料列相關聯的原始聯絡人郵寄地址。 原始聯絡人可以有多個郵寄地址。
ContactsContract.CommonDataKinds.GroupMembership 將原始聯絡人連結至聯絡資訊供應器中其中一個群組的 ID。 群組是帳戶類型和帳戶名稱的選用功能。如需詳細說明,請參閱「聯絡人群組」一節。

聯絡人

聯絡人供應程式會合併所有帳戶類型和帳戶名稱的原始聯絡人列 建立聯絡人。這有助於顯示和修改 使用者收集的資料聯絡人提供者管理新聯絡人的建立作業 以及與現有聯絡資料列彙整的原始聯絡人。應用程式和同步處理轉接器都無法新增聯絡人,且聯絡人資料列中的部分欄為唯讀。

注意:如果您嘗試使用 insert() 將聯絡人新增至聯絡資訊供應器,系統會傳回 UnsupportedOperationException 例外狀況。如果嘗試更新資料欄 請先列為「唯讀」即可忽略更新。

聯絡人供應程式會建立新的聯絡人,以新增原始聯絡人 與任何現有聯絡人不符假如有原始原始檔案 聯絡人的資料變更,因此不再與原本要連結的聯絡人相符 先前附檔如果應用程式或同步轉換介面會建立新的原始聯絡人, 「符合」的現有聯絡人,則新的原始聯絡人會匯總至現有聯絡人 聯絡。

聯絡人供應器會透過 Contacts 資料表中聯絡人資料列的 _ID 欄,將聯絡人資料列連結至原始聯絡人資料列。原始聯絡人表格的 CONTACT_IDContactsContract.RawContacts包含以下項目的 _ID 個值: 與各個原始聯絡人列相關聯的聯絡人列。

ContactsContract.Contacts 資料表也有該資料欄 LOOKUP_KEY是 「永久」這個連結。因為聯絡人供應程式負責維護聯絡人 這項功能可能會變更聯絡人列的 _ID 值 回應匯總或同步處理作業即使發生這種情況 CONTENT_LOOKUP_URI與 聯絡人的LOOKUP_KEY仍會 指向聯絡人列,即可使用 LOOKUP_KEY 保持連結至「我的最愛」聯絡人等。這個資料欄的格式有: 與 _ID 欄的格式無關。

圖 3 顯示三個主要資料表之間的關聯。

聯絡人供應程式的主要資料表

圖 3. 聯絡人、原始聯絡人和詳細資料表格的關係。

注意: 如果您將應用程式發布到 Google Play 商店, 請注意,應用程式執行的裝置搭載 Android 10 (API 級別 29) 以上版本。 屆時,只有一部分聯絡人資料欄位和方法已過時

在上述情況下,系統會定期清除任何值 寫入這些資料欄位:

用來設定上述資料欄位的 API 也已淘汰:

此外,下列欄位也不再傳回常用聯絡人。請注意,只有在聯絡人屬於特定資料類型時,這些欄位才會影響聯絡人的排名。

如果您的應用程式會存取或更新這些欄位或 API,請使用其他方法。例如,您可以使用 私人 內容供應器,或儲存在應用程式或後端的其他資料 有些人會將 Cloud Storage 視為檔案系統 但實際上不是

如要確認應用程式的功能不會受到這項變更的影響,您可以手動清除這些資料欄位。如要執行此操作,請在搭載 Android 4.1 (API 級別 16) 以上版本的裝置上執行下列 ADB 指令:

adb shell content delete \
--uri content://com.android.contacts/contacts/delete_usage

來自同步轉換介面的資料

使用者會將聯絡人資料直接輸入裝置,但資料也會傳送至聯絡人 透過同步轉換介面由網路服務提供的供應商 (自動同步) 裝置與服務之間移轉資料。同步處理轉接器會在系統的控管下在背景執行,並呼叫 ContentResolver 方法來管理資料。

在 Android 中,同步處理轉接器所使用的網路服務會透過帳戶類型識別。每個同步處理轉接程式都適用於一種帳戶類型,但可以支援多個帳戶名稱 就可以選擇那些類型的物件本節將簡單介紹帳戶類型和帳戶名稱 原始聯絡人資料的來源。以下定義提供更多詳細資訊,並說明帳戶類型和名稱與同步處理轉接器和服務的關聯性。

帳戶類型
用於識別使用者要在當中儲存資料的服務。大多數的情況下,使用者必須 進行驗證舉例來說,Google 聯絡人是一種帳戶類型 代碼 google.com。這個值與 AccountManager
帳戶名稱
識別特定帳戶或帳戶類型。Google 聯絡人帳戶 與擁有電子郵件地址做為帳戶名稱的 Google 帳戶相同。 其他服務可能會使用單字使用者名稱或數字 ID。

帳戶類型不必是唯一值。使用者可以設定多個 Google 聯絡人帳戶,並將資料下載至聯絡資訊提供者。如果使用者有一個個人帳戶名稱的個人聯絡人,以及另一個工作帳戶名稱的聯絡人,就可能會發生這種情況。帳戶名稱為 通常不重複這兩者會共同識別聯絡人供應工具與外部服務之間的特定資料流。

如要將服務的資料轉移至聯絡人供應程式,您必須編寫 自己的同步轉換器。本節將詳細說明 聯絡人供應商同步轉換介面

圖 4 顯示聯絡人提供者如何融入人物資料的資料流程。在標示為「同步轉換器」的方塊中每個轉接程式都會依帳戶類型加上標籤。

使用者相關資料流

圖 4. 聯絡人供應程式的資料流程。

所需權限

應用程式如要存取聯絡人供應程式,必須要求提供下列資料 權限:

具備一或多個資料表的讀取權限
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。舉例來說,下列程式碼片段會擷取設定檔資料:

Kotlin

// Sets the columns to retrieve for the user profile
projection = arrayOf(
        ContactsContract.Profile._ID,
        ContactsContract.Profile.DISPLAY_NAME_PRIMARY,
        ContactsContract.Profile.LOOKUP_KEY,
        ContactsContract.Profile.PHOTO_THUMBNAIL_URI
)

// Retrieves the profile from the Contacts Provider
profileCursor = contentResolver.query(
        ContactsContract.Profile.CONTENT_URI,
        projection,
        null,
        null,
        null
)

Java

// Sets the columns to retrieve for the user profile
projection = new String[]
    {
        Profile._ID,
        Profile.DISPLAY_NAME_PRIMARY,
        Profile.LOOKUP_KEY,
        Profile.PHOTO_THUMBNAIL_URI
    };

// Retrieves the profile from the Contacts Provider
profileCursor =
        getContentResolver().query(
                Profile.CONTENT_URI,
                projection ,
                null,
                null,
                null);

注意:如果您擷取多個聯絡人列,且想判斷其中一個資料列, 是使用者設定檔,測試該資料列的 IS_USER_PROFILE 欄。如果聯絡人是使用者設定檔,這個欄會設為「1」。

聯絡人供應程式中繼資料

聯絡人供應程式會管理用於追蹤存放區中聯絡人資料狀態的資料。這個存放區相關中繼資料會儲存在多個位置,包括原始聯絡人、資料和聯絡人資料表列、ContactsContract.Settings 資料表和 ContactsContract.SyncState 資料表。下表顯示了 狀態的改變:

表 3. 聯絡人供應程式中的中繼資料

表格 Column 意義
ContactsContract.RawContacts DIRTY 「0」- 自上次同步後沒有變更。 標示在裝置上已變更且必須同步重新同步處理的原始聯絡人 伺服器當 Android 應用程式更新資料列時,聯絡人提供者會自動設定這個值。

修改原始聯絡人或資料表的同步轉換介面必須一律附加 將 CALLER_IS_SYNCADAPTER 字串傳送至 應用程式的內容 URI如此可防止供應商將資料列標示為骯髒的資料列。 否則,對同步轉換程式的修改看起來就會是本機修改, 。

「1」- 自上次同步後有所變更,需要同步回伺服器。
ContactsContract.RawContacts VERSION 此列的版本號碼。 每當資料列或相關資料發生變更時,Contacts 提供者就會自動遞增這個值。
ContactsContract.Data DATA_VERSION 此列的版本號碼。 只要資料列有變更,Contacts 供應器就會自動遞增這個值。
ContactsContract.RawContacts SOURCE_ID 這個字串值可用於識別此原始聯絡人與建立該聯絡人的帳戶。 當同步處理器建立新的原始聯絡資訊時,這個欄應設為原始聯絡資訊的伺服器專屬 ID。當 Android 應用程式建立新的 原始聯絡人,應用程式應將此欄留空。這會向同步處理轉接器發出訊號,表示應在伺服器上建立新的原始聯絡人,並取得 SOURCE_ID 的值。

特別是,每個帳戶類型的來源 ID 都必須不重複,且在同步處理期間應保持穩定:

  • 唯一:帳戶的每個原始聯絡人必須有專屬的來源 ID。如果發生以下情況: 您不強制執行此設定,否則您在聯絡人應用程式會造成問題。 請注意,相同帳戶類型的兩個原始聯絡人,可能 具有相同來源 ID。例如:原始聯絡人「Thomas Higginson」的 「emily.dickinson@gmail.com」帳戶可擁有相同的來源 使用 ID 做為原始聯絡人「Thomas Higginson」這個帳戶 emilyd@gmail.com
  • 穩定版:來源 ID 是線上服務資料中永久的一部分, 原始聯絡人舉例來說,如果使用者清除 應用程式設定並重新同步處理後,還原的原始聯絡人應該就會相同 和先前一樣的來源 ID如果您未強制執行這項設定,捷徑就會停止運作。
ContactsContract.Groups GROUP_VISIBLE 「0」:這個群組中的聯絡人應不會顯示在 Android 應用程式 UI 中。 這個欄位可與允許使用者隱藏特定群組聯絡人的伺服器相容。
「1」:這個群組中的聯絡人可在應用程式 UI 中顯示。
ContactsContract.Settings UNGROUPED_VISIBLE 「0」- 在這個帳戶和帳戶類型中,不屬於群組的聯絡人是 不會顯示在 Android 應用程式 UI 中。 根據預設,如果聯絡人的原始聯絡人皆未屬於任何群組,該聯絡人就會隱藏 (原始聯絡人的群組成員資格,可由 ContactsContract.Data 資料表中一或多個 ContactsContract.CommonDataKinds.GroupMembership 資料列表示)。您可以在帳戶類型和帳戶的 ContactsContract.Settings 表格資料列中設定這個標記,強制顯示沒有群組的聯絡人。這個旗標其中一個用途是顯示來自未使用群組的伺服器聯絡人。
「1」- 在這個帳戶和帳戶類型中,不屬於群組的聯絡人是 顯示在應用程式 UI 中
ContactsContract.SyncState (全部) 使用這個表格儲存同步轉接程式的中繼資料。 你可以透過這個表格永久儲存同步處理狀態和其他同步相關資料 裝置。

聯絡人提供者存取權

本節說明從聯絡人供應程式存取資料的指南,著重於 包括:

  • 實體查詢。
  • 批次修改。
  • 使用意圖擷取及修改資料。
  • 資料完整性。

本節中也會詳細說明從同步轉換介面進行修改, 聯絡人供應商同步轉換介面

查詢實體

由於聯絡人供應器資料表是按階層排序,因此通常會擷取資料列和與其相關聯的所有「子」資料列。例如 建議您擷取某個人物的所有資訊 一筆 ContactsContract.RawContactsContactsContract.Contacts 列,或所有的 一筆 ContactsContract.CommonDataKinds.EmailContactsContract.RawContacts 列。為方便起見,這份 提供者提供實體結構,就像資料庫彙整 。

實體就像是資料表,由父項資料表和子項資料表的所選欄所組成。 查詢實體時,您需要根據資料欄提供投影和搜尋條件 才能從實體取得結果是 Cursor,其中包含每個擷取的子資料表資料列。例如,如果您查詢 ContactsContract.Contacts.Entity表示聯絡人姓名 且所有代表權重的 ContactsContract.CommonDataKinds.Email 列 該名稱的原始聯絡人,則會傳回一個 Cursor,其中包含一列 每個 ContactsContract.CommonDataKinds.Email

實體簡化查詢作業。您可以使用實體擷取 不必先查詢父項資料表, 那麼就必須用這個 ID 查詢子項資料表。此外,通訊錄提供者會在單一交易中處理針對實體的查詢,確保擷取的資料在內部一致。

注意:實體通常不會包含父項和 子資料表。如果您嘗試使用不在實體的欄名常數清單中的欄名,系統會傳回 Exception

以下程式碼片段說明如何擷取聯絡人的所有原始聯絡人資料列。程式碼片段 是大型應用程式,其中包含兩個活動「主要」和「詳細資料」主要活動 會顯示聯絡列清單;使用者選取時,活動會將 ID 傳送至詳細資料 活動。詳細資料活動會使用 ContactsContract.Contacts.Entity 顯示與所選聯絡人相關聯的所有原始聯絡人的所有資料列。

這段程式碼擷取自「詳細資料」活動:

Kotlin

...
    /*
     * 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).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY
    )

    // Initializes the loader identified by LOADER_ID.
    loaderManager.initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this        // The context of the activity
    )

    // Creates a new cursor adapter to attach to the list view
    cursorAdapter = SimpleCursorAdapter(
            this,                       // the context of the activity
            R.layout.detail_list_item,  // the view item containing the detail widgets
            mCursor,                    // the backing cursor
            fromColumns,               // the columns in the cursor that provide the data
            toViews,                   // the views in the view item that display the data
            0)                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.adapter = cursorAdapter
...
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
    /*
     * 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.
     */
    val projection: Array<String> = arrayOf(
            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.
     */
    val 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 CursorLoader(
            applicationContext, // The activity's context
            contactUri,        // The entity content URI for a single contact
            projection,         // The columns to retrieve
            null,               // Retrieve all the raw contacts and their data rows.
            null,               //
            sortOrder           // Sort by the raw contact ID.
    )
}

Java

...
    /*
     * 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).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);

    // Initializes the loader identified by LOADER_ID.
    getLoaderManager().initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this);      // The context of the activity

    // Creates a new cursor adapter to attach to the list view
    cursorAdapter = new SimpleCursorAdapter(
            this,                        // the context of the activity
            R.layout.detail_list_item,   // the view item containing the detail widgets
            mCursor,                     // the backing cursor
            fromColumns,                // the columns in the cursor that provide the data
            toViews,                    // the views in the view item that display the data
            0);                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.setAdapter(cursorAdapter);
...
@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 context
            contactUri,              // The entity content URI for a single contact
            projection,               // The columns to retrieve
            null,                     // Retrieve all the raw contacts and their data rows.
            null,                     //
            sortOrder);               // Sort by the raw contact ID.
}

載入完成時,LoaderManager 會叫用 onLoadFinished()。這個方法的傳入引數其中一個是 Cursor 取代為查詢結果。您可以在自己的應用程式中,從這個 Cursor 取得資料,以便顯示或進一步處理。

批次修改

請盡可能在下列位置插入、更新及刪除聯絡人提供者的資料: 建立「批次模式」的 ArrayList ContentProviderOperation 物件和呼叫 applyBatch()。由於 聯絡人供應程式會在 單個applyBatch() 所以您的修改內容永遠不會使聯絡人存放區不一致 時間。批次修改功能還可同時插入原始聯絡人及其詳細資料。

注意:如要修改「單一」原始聯絡人,請考慮將意圖傳送至 而非處理應用程式中的修改內容。 本節將詳細說明如何執行此操作 使用意圖擷取及修改

收益點

包含大量作業的批次修改可能會阻擋其他程序 導致整體使用者體驗不佳如要將要執行的所有修改項目整理到盡可能少的個別清單中,同時避免這些項目阻斷系統,您應為一或多項作業設定產生點。產生點是 ContentProviderOperation 物件,其 isYieldAllowed() 值設為 true。當聯絡人供應器遇到產生點時,會暫停工作,讓其他程序執行並關閉目前的交易。供應商再次啟動後 繼續執行 ArrayList 中的下一個作業,並啟動新的作業 交易。

收益點會導致每次呼叫 applyBatch()。因此,您應為一組相關資料列的最後一項作業設定產生點。舉例來說,您應該為加入原始聯絡資料列及其相關資料列的集合,或與單一聯絡人相關的資料列集合,設定最後作業的產生點。

收益點也是原子運算的單位。兩種收益點之間所有存取行為 是成功或失敗的單一單元如果沒有設定任何收益點 不可分割運算為整批作業。如果您使用交出點,就能避免作業降低系統效能,同時確保部分作業是原子作業。

修改參考資料

當您要插入新的原始聯絡人資料列及其相關聯的資料列做為一組 ContentProviderOperation 物件時,您必須將原始聯絡人的 _ID 值插入做為 RAW_CONTACT_ID 值,以便將資料列連結至原始聯絡人資料列。不過,如果您要為資料列建立 ContentProviderOperation,就無法使用這個值,因為您尚未為原始聯絡人列套用 ContentProviderOperation。如要解決這個問題 ContentProviderOperation.Builder 類別具有以下方法 withValueBackReference()。 這個方法可讓您使用先前作業的結果插入或修改資料欄。

withValueBackReference() 方法有兩個引數:

key
鍵/值組合的鍵。這個引數的值應為您要修改的資料表中資料欄的名稱。
previousResult
指定陣列中的值從 0 開始的索引 ContentProviderResult 個物件來自 applyBatch()。阿斯 而每一項作業的結果都會儲存在 結果中繼陣列。previousResult 值是其中一個結果的索引,會透過 key 值擷取並儲存。這樣一來,您就能插入新的原始聯絡人記錄並取得其 _ID 值,然後在新增 ContactsContract.Data 列時對該值建立「回溯參照」。

您首次呼叫 applyBatch() 時會建立整個結果陣列,其大小等於您提供的 ContentProviderOperation 物件 ArrayList 的大小。不過,結果陣列中的所有元素都會設為 null,如果您嘗試對尚未套用的作業進行結果回溯參照,withValueBackReference() 就會擲回 Exception

下列程式碼片段說明如何批次插入新的原始聯絡人和資料。他們 包括可以建立收益點並使用返回參照的程式碼。

第一個程式碼片段會從使用者介面擷取聯絡人資料。此時,使用者已在 選取要新增原始聯絡人的帳戶。

Kotlin

// Creates a contact entry from the current UI values, using the currently-selected account.
private fun createContactEntry() {
    /*
     * Gets values from the UI
     */
    val name = contactNameEditText.text.toString()
    val phone = contactPhoneEditText.text.toString()
    val email = contactEmailEditText.text.toString()

    val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition]

    val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]

Java

// Creates a contact entry from the current UI values, using the currently-selected account.
protected void createContactEntry() {
    /*
     * Gets values from the UI
     */
    String name = contactNameEditText.getText().toString();
    String phone = contactPhoneEditText.getText().toString();
    String email = contactEmailEditText.getText().toString();

    int phoneType = contactPhoneTypes.get(
            contactPhoneTypeSpinner.getSelectedItemPosition());

    int emailType = contactEmailTypes.get(
            contactEmailTypeSpinner.getSelectedItemPosition());

下列程式碼片段會建立作業,將原始聯絡資料列插入 ContactsContract.RawContacts 資料表:

Kotlin

    /*
     * 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.
    val ops = arrayListOf<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.
     */
    var op: ContentProviderOperation.Builder =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.name)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.type)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

Java

    /*
     * 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, selectedAccount.getType())
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName());

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

接下來,程式碼會為顯示名稱、電話和電子郵件地址建立資料列。

每個作業建構工具物件都會使用 withValueBackReference() 取得 RAW_CONTACT_ID。參照點會回溯至第一個作業的 ContentProviderResult 物件,該作業會新增原始聯絡人資料列,並傳回新的 _ID 值。因此,每個資料列都會透過其 RAW_CONTACT_ID 自動連結至所屬的新 ContactsContract.RawContacts 資料列。

用於新增電子郵件列的 ContentProviderOperation.Builder 物件是 並加上 withYieldAllowed(),這樣就會設定收益點:

Kotlin

    // Creates the display name for the new raw contact, as a StructuredName data row.
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

    // Inserts the specified phone number and type as a Phone data row
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

    // Inserts the specified email and type as a Phone data row
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType)

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

Java

    // Creates the display name for the new raw contact, as a StructuredName data row.
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified phone number and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified email and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

最後一個程式碼片段顯示對 applyBatch() 的呼叫,該呼叫會插入新的原始聯絡人和資料列。

Kotlin

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG, "Selected account: ${mSelectedAccount.name} (${mSelectedAccount.type})")
    Log.d(TAG, "Creating contact: $name")

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {
        contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
    } catch (e: Exception) {
        // Display a warning
        val txt: String = getString(R.string.contactCreationFailure)
        Toast.makeText(applicationContext, txt, Toast.LENGTH_SHORT).show()

        // Log exception
        Log.e(TAG, "Exception encountered while inserting contact: $e")
    }
}

Java

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG,"Selected account: " + selectedAccount.getName() + " (" +
            selectedAccount.getType() + ")");
    Log.d(TAG,"Creating contact: " + name);

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {

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

            // Display a warning
            Context ctx = getApplicationContext();

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

            // Log exception
            Log.e(TAG, "Exception encountered while inserting contact: " + e);
    }
}

批次作業也可讓您實作最佳化並行控制, 一種套用修改交易的方法,而不必鎖定基礎存放區。 如要使用這個方法,您必須套用交易,然後檢查是否有其他可能同時進行的修改。如果發現發生不一致的修改,請復原交易並重試。

最佳化並行控制對於行動裝置來說非常實用,因為行動裝置只有一個使用者 而且不能同時存取資料存放區。由於不使用鎖定功能 也不用浪費時間設定鎖定或等待其他交易解除鎖定。

如要在更新單一 ContactsContract.RawContacts 資料列時使用樂觀並行控制,請按照下列步驟操作:

  1. 擷取原始聯絡人的 VERSION 欄,以及您擷取的其他資料。
  2. 使用 newAssertQuery(Uri) 方法建立適合強制限制的 ContentProviderOperation.Builder 物件。針對內容 URI 使用「RawContacts.CONTENT_URI」 並附加原始聯絡人的 _ID
  3. 對於 ContentProviderOperation.Builder 物件,請呼叫 withValue():比較VERSION 降到剛剛擷取的版本號碼
  4. 針對相同的 ContentProviderOperation.Builder,請呼叫 withExpectedCount(),確保這項斷言只測試一個資料列。
  5. 呼叫 build() 即可建立 ContentProviderOperation 物件,然後將這個物件新增為 ArrayList 中的第一個物件,您傳遞至 applyBatch()
  6. 套用批次交易。

當您在讀取資料列與 就會發生「裁判」ContentProviderOperation 將會失敗,並且會退回整批作業。接著,您可以選擇重試批次或採取其他動作。

下列程式碼片段示範如何在使用 CursorLoader 查詢單一原始聯絡人後,建立「斷言」ContentProviderOperation

Kotlin

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID))
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION))
}

...

// Sets up a Uri for the assert operation
val rawContactUri: Uri = ContentUris.withAppendedId(
        ContactsContract.RawContacts.CONTENT_URI,
        rawContactID
)

// Creates a builder for the assert operation
val assertOp: ContentProviderOperation.Builder =
        ContentProviderOperation.newAssertQuery(rawContactUri).apply {
            // Adds the assertions to the assert operation: checks the version
            withValue(SyncColumns.VERSION, mVersion)

            // and count of rows tested
            withExpectedCount(1)
        }

// Creates an ArrayList to hold the ContentProviderOperation objects
val ops = arrayListOf<ContentProviderOperation>()

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 {
    val results: Array<ContentProviderResult> = contentResolver.applyBatch(AUTHORITY, ops)
} catch (e: OperationApplicationException) {
    // Actions you want to take if the assert operation fails go here
}

Java

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    // Gets the raw contact's _ID and VERSION values
    rawContactID = 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, rawContactID);

// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.newAssertQuery(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<ContentProviderOperation>;

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
    }

使用意圖擷取及修改

傳送意圖至裝置的聯絡人應用程式,即可存取「聯絡人」 間接供應商。意圖會啟動裝置的聯絡人應用程式 UI,使用者可以在這裡 執行與聯絡人相關的工作使用者可以透過這類存取權:

  • 從清單中選取聯絡人,並將其傳回至應用程式以便進一步處理。
  • 編輯現有聯絡人的資料。
  • 為任何帳戶插入新的原始聯絡人。
  • 刪除聯絡人或聯絡人資料。

如果使用者正在插入或更新資料,您可以先收集資料,再以 意圖的一部分

當您使用意圖透過裝置的聯絡人應用程式存取聯絡人提供者時,不必自行編寫 UI 或程式碼來存取提供者。您也不必 要求讀取或寫入供應器的權限。裝置上的聯絡人應用程式 將讀取權限委派給聯絡人,同時修改 應用程式提供者,就不必具備寫入權限。

如需傳送意圖存取供應器的一般程序,請參閱 內容供應器基本概念指南,位於「透過意圖存取資料」一節。動作 表 4 匯總了 MIME 類型和可用工作所使用的資料值,而 可搭配使用的額外值 putExtra()已列於 ContactsContract.Intents.Insert 的參考說明文件:

表 4. 聯絡人供應程式意圖。

工作 動作 資料 MIME 類型 附註
從清單中選擇聯絡人 ACTION_PICK 下列其中一項: 未使用 視您提供的內容 URI 類型而定,系統會顯示原始聯絡人清單或原始聯絡人的資料清單。

致電 startActivityForResult(), ,會傳回所選列的內容 URI。URI 的格式是資料表的內容 URI,並附加資料列的 LOOKUP_ID。裝置的聯絡人應用程式會將讀取和寫入權限委派給這個內容 URI,直到活動結束為止。詳情請參閱「內容供應者基本資訊」指南。

插入新的原始聯絡資訊 Insert.ACTION RawContacts.CONTENT_TYPE,MIME 類型,適用於一組原始聯絡人。 顯示裝置聯絡人應用程式的「新增聯絡人」畫面。系統會顯示您新增至意圖的額外值。如果透過 startActivityForResult() 傳送,新增的原始聯絡人的內容 URI 會傳回至活動的 onActivityResult() 回呼方法,並在 Intent 引數的「data」欄位中傳回。如要取得值,請呼叫 getData()
編輯聯絡人 ACTION_EDIT CONTENT_LOOKUP_URI: 聯絡人。這個編輯者活動可讓使用者編輯任何相關聯的資料 與這位聯絡人聯絡 Contacts.CONTENT_ITEM_TYPE,單一聯絡人。 在聯絡人應用程式中顯示「編輯聯絡人」畫面。系統會顯示您新增至意圖的額外值。當使用者按一下 [完成] 儲存 您的活動就會回到前景。
顯示也可以新增資料的挑選器。 ACTION_INSERT_OR_EDIT CONTENT_ITEM_TYPE 這個意圖一律會顯示聯絡人應用程式的挑選畫面。使用者可以 選擇要編輯的聯絡人,或是新增聯絡人。編輯內容或新增畫面 顯示的內容 (取決於使用者的選擇),以及您在意圖中傳遞的額外資料。 。如果您的應用程式會顯示電子郵件或電話號碼等聯絡人資料,請使用這個意圖,讓使用者將資料新增至現有的聯絡人。聯絡人

注意:您不需要在這項意圖的額外項目中傳送名稱值。 因為使用者總是選擇現有名稱或新增名稱此外,如果您傳送名稱,而使用者選擇編輯,聯絡人應用程式會顯示您傳送的名稱,覆寫先前的值。如果使用者沒有注意到這點,並儲存編輯內容,舊值就會遺失。

裝置的聯絡人應用程式不允許您使用意圖刪除原始聯絡人或任何相關資料。如果您想刪除原始聯絡人,請使用 ContentResolver.delete()ContentProviderOperation.newDelete()

下列程式碼片段說明如何建構並傳送插入新原始資料的意圖 聯絡人與資料:

Kotlin

// Gets values from the UI
val name = contactNameEditText.text.toString()
val phone = contactPhoneEditText.text.toString()
val email = contactEmailEditText.text.toString()

val company = companyName.text.toString()
val jobtitle = jobTitle.text.toString()

/*
 * 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
val contactData = arrayListOf<ContentValues>()

/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
val rawContactRow = ContentValues().apply {
    // Adds the account type and name to the row
    put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.type)
    put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.name)
}

// Adds the row to the array
contactData.add(rawContactRow)

/*
 * Sets up the phone number data row
 */

// Sets up the row as a ContentValues object
val phoneRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

    // Adds the phone number and its type to the row
    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
val emailRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

    // Adds the email address and its type to the row
    put(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
}

// Adds the row to the array
contactData.add(emailRow)

// Creates a new intent for sending to the device's contacts application
val insertIntent = Intent(ContactsContract.Intents.Insert.ACTION).apply {
    // Sets the MIME type to the one expected by the insertion activity
    type = ContactsContract.RawContacts.CONTENT_TYPE

    // Sets the new contact name
    putExtra(ContactsContract.Intents.Insert.NAME, name)

    // Sets the new company and job title
    putExtra(ContactsContract.Intents.Insert.COMPANY, company)
    putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle)

    /*
    * 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
    */
    putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData)
}

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent)

Java

// Gets values from the UI
String name = contactNameEditText.getText().toString();
String phone = contactPhoneEditText.getText().toString();
String email = contactEmailEditText.getText().toString();

String company = companyName.getText().toString();
String jobtitle = jobTitle.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, selectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.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.CommonDataKinds.StructuredName 列 每新增 ContactsContract.RawContacts
ContactsContract.RawContacts 沒有 共有 ContactsContract.CommonDataKinds.StructuredNameContactsContract.Data資料表可能會導致以下期間發生問題: 匯總。
請一律將新的 ContactsContract.Data 資料列連結至其父項 ContactsContract.RawContacts 資料列。
未連結至 ContactsContract.RawContactsContactsContract.Data 資料列不會顯示在裝置的聯絡人應用程式中,且可能會導致同步處理轉接器發生問題。
只變更您擁有的原始聯絡人資料。
請注意,聯絡人供應商通常會管理多種不同帳戶類型/線上服務的資料。您必須確保應用程式只修改 或刪除屬於您的資料,而且該資料列只會以 帳戶類型和由您自行控管的名稱
請一律使用 ContactsContract 和其子類別中定義的常數,用於授權、內容 URI、URI 路徑、資料欄名稱、MIME 類型和 TYPE 值。
使用這些常數有助於避免錯誤。如果任何常數已淘汰,您也會收到編譯器警告通知。

自訂資料列

建立及使用自訂 MIME 類型,即可插入、編輯、刪除及擷取 自己的資料列在「ContactsContract.Data」資料表中。您的資料列 只能使用 ContactsContract.DataColumns,但你可以自行對應 指定類型專屬的資料欄名稱,預設資料欄名稱。在裝置的聯絡人應用程式中, 您可以查看各列的資料,但無法編輯或刪除,使用者也無法新增 其他資料。如要允許使用者修改自訂資料列,您必須在自己的應用程式中提供編輯器活動。

如要顯示自訂資料,請提供含有 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。這麼做可讓系統對轉接程式的方法進行跨程序呼叫。
實際的同步轉換介面,實作為 AbstractThreadedSyncAdapter 的具體子類別。
This 類別會執行從伺服器下載資料、從裝置上傳資料,以及解決衝突的工作。轉接器的主要工作是 透過方法 onPerformSync() 完成這個類別必須以單例模式例項化。
Application 的子類別。
This class acts as a factory for the sync adapter singleton. 使用 onCreate() 方法,將同步轉換介面執行個體化 提供靜態的「getter」方法,將單例模式傳回 同步轉換介面的 onBind() 方法 課程中也會快速介紹 Memorystore 這是 Google Cloud 的全代管 Redis 服務
選用:回應的 Service 元件 以及系統拒絕使用者驗證的要求。
AccountManager」會啟動這項服務開始驗證程序 上傳資料集之後,您可以運用 AutoML 自動完成部分資料準備工作服務的 onCreate() 方法會將 驗證器物件當系統想要針對 應用程式的同步轉換器,會呼叫服務的 onBind() 方法,即可取得 IBinder 代表驗證器。這樣一來,系統就能 跨程序呼叫驗證器方法。
選用:具體的子類別 處理以下項目的 AbstractAccountAuthenticator 要求: 驗證。
這個類別提供 AccountManager 叫用的方法 透過伺服器驗證使用者的憑證。驗證程序的詳細資料會因使用的伺服器技術而有很大差異。請 請參閱伺服器軟體的說明文件,進一步瞭解驗證。
定義系統同步處理介接程式和驗證工具的 XML 檔案。
上述的同步轉換介面和驗證器服務元件 <service> 元素。這些元素 包含 <meta-data> 提供特定資料給系統的子元素:
  • 同步處理器轉接程式服務的 <meta-data> 元素會指向 XML 檔案 res/xml/syncadapter.xml。而這個檔案會指定 與聯絡人供應程式同步處理的網路服務 URI 以及網路服務的帳戶類型。
  • 選用:驗證工具的 <meta-data> 元素會指向 XML 檔案 res/xml/authenticator.xml。而這個檔案會指定 這個驗證工具支援的帳戶類型,以及 會出現這種情形。此元素中指定的帳戶類型必須與同步處理轉接程式指定的帳戶類型相同。

社交串流資料

android.provider.ContactsContract.StreamItems 與 android.provider.ContactsContract.StreamItemPhotos 資料表 管理從社交網路傳入的資料。您可以編寫同步處理器,將來自您自己的網路的串流資料新增至這些資料表,也可以從這些資料表讀取串流資料,並在自己的應用程式中顯示,或者同時執行這兩項操作。有了這些功能,您就能將社交網路服務和應用程式整合至 Android 的社交網路體驗。

社群訊息串文字

訊息串項目一律會與原始聯絡人建立關聯。android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID 會連結至原始聯絡人的 _ID 值。原始帳戶的類型和帳戶名稱 也會儲存在訊息串項目列中。

將串流中的資料儲存在下列欄中:

android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
必要。與這個串流項目相關聯的原始聯絡人的使用者帳戶類型。請記得在插入串流項目時設定這個值。
android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
必要。與此串流項目相關聯的原始聯絡人的使用者帳戶名稱。插入串流項目時,請務必設定這個值。
ID 欄
必要。插入串流項目時,您必須插入下列 ID 欄:
  • android.provider.ContactsContract.StreamItemsColumn#CONTACT_ID: 這個串流的聯絡人 android.provider.BaseColumn#_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.StreamItemsColumn#TEXT
訊息串項目的文字,可能是項目來源張貼的內容 或是產生串流項目的部分動作說明。這一欄可包含 可以轉譯任何格式和嵌入資源圖片 fromHtml()。供應商可能會截斷或 編輯長篇內容,但盡量避免破壞標記。
android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
文字字串,其中包含串流項目插入或更新的時間,以自紀元起算的毫秒為單位。插入或更新串流項目的應用程式負責維護這個資料欄,Contacts 提供者不會自動維護這個資料欄。

如要顯示串流項目的識別資訊,請使用 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 資料表的 _ID 欄值。相片參考會儲存在 資料表:

android.provider.ContactsContract.StreamItemPhotos#PHOTO 欄 (BLOB)。
相片的二進位表示法,由供應商調整大小,以便儲存和顯示。本欄的用途是提供舊版聯絡人的回溯相容性 用來儲存相片的供應商。不過,在目前的版本中,您不應使用這個資料欄來儲存相片。而是改用 android.provider.ContactsContract.StreamItemPhotosColumn#PHOTO_FILE_ID,或 android.provider.ContactsContract.StreamItemPhotosColumn#PHOTO_URI 詳見下文) 即可將相片儲存在檔案中。這個資料欄現在包含可供讀取的相片縮圖。
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
原始聯絡人的相片數字 ID。將這個值附加至常數 DisplayPhoto.CONTENT_URI,以取得指向單一相片檔案的內容 URI,然後呼叫 openAssetFileDescriptor() 以取得相片檔案的句柄。
android.provider.ContactsContract.StreamItemPhotosColumn#PHOTO_URI
內容 URI,直接指向這一列所代表相片的相片檔案。 使用這個 URI 呼叫 openAssetFileDescriptor(),取得相片檔案的控制代碼。

使用社群串流資料表

這些資料表的運作方式與聯絡資訊提供者的其他主要資料表相同,但有以下差異:

  • 這些資料表需要額外的存取權限。如要讀取這些資料,應用程式必須具備 android.Manifest.permission#READ_SOCIAL_STREAM 權限。目的地: 應用程式必須具備權限 android.Manifest.permission#WRITE_SOCIAL_STREAM。
  • 對於 android.provider.ContactsContract.StreamItems 資料表,每個原始聯絡人的資料列數量皆有限制。一旦達到這個上限,Contacts 提供者會自動刪除最舊的 android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP 資料列,騰出空間給新的串流項目資料列。若要取得 限制,向內容 URI 發出查詢 android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI。您可以將內容 URI 以外的所有引數設為 null。這項查詢會傳回包含單一資料列的游標,其中包含單一資料欄 android.provider.ContactsContract.StreamItems#MAX_ITEMS。

android.provider.ContactsContract.StreamItems.StreamItemPhotos 類別定義了 android.provider.ContactsContract.StreamItemPhotos 的子表格,其中包含單一串流項目的相片列。

社群串流互動

聯絡人供應器管理的社群串流資料,搭配裝置的聯絡人應用程式,可提供強大的功能,將您的社群網路系統與現有聯絡人連結。可用的功能如下:

  • 您可以使用同步處理轉接器,將社交網路服務同步至聯絡人提供者,藉此擷取使用者聯絡人的近期活動,並儲存在 android.provider.ContactsContract.StreamItems 和 android.provider.ContactsContract.StreamItemPhotos 資料表中,以供日後使用。
  • 除了定期同步作業之外,您還可以觸發同步處理器,在使用者選取要查看的聯絡人時擷取其他資料。這樣一來,同步處理的適配器就能擷取高解析度相片,以及聯絡人的最新串流項目。
  • 您可以透過裝置的聯絡人應用程式和聯絡人提供者註冊通知,在查看聯絡人時接收意圖,並在該時點透過服務更新聯絡人的狀態。這種做法可能更快,使用較少 頻寬會比使用同步轉換介面執行完整同步。
  • 使用者在查看聯絡人時,可將聯絡人新增至你的社群網路服務 在裝置的聯絡人應用程式中。透過「邀請聯絡人」即可啟用這項功能特徵 結合將現有聯絡人新增至 網路,以及 XML 檔案,該檔案提供裝置的聯絡人應用程式及 請與供應商聯絡,瞭解您的應用程式詳細資訊。

與聯絡人供應器定期同步處理串流項目的做法與其他同步處理作業相同。如要進一步瞭解同步處理,請參閱「聯絡人供應器同步處理轉接器」一節。註冊通知和邀請聯絡人,請參閱接下來的兩個部分。

註冊以處理社交網路檢視畫面

如要註冊同步處理介面,以便在使用者查看由同步處理介面管理的聯絡人時收到通知,請按照下列步驟操作:

  1. 在專案的 res/xml/ 中建立名為 contacts.xml 的檔案 目錄。如果您已擁有這個檔案,可以略過這個步驟。
  2. 在這個檔案中,新增 <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> 元素。如果已有這個元素,可以略過這個步驟。
  3. 如何註冊服務,以便在使用者開啟聯絡人詳細資料頁面時接收通知 在裝置的「聯絡人」應用程式中,新增 將 viewContactNotifyService="serviceclass" 新增至元素,其中 serviceclass 是服務的完整類別名稱 ,且應接收來自裝置聯絡人應用程式的意圖。針對通知器服務,請使用擴充 IntentService 的類別,讓服務接收意圖。傳入意圖中的資料包含原始版本的內容 URI 與使用者點選的聯絡人聯絡。您可以從通知器服務繫結並呼叫同步處理適配器,以便更新原始聯絡人的資料。

如要註冊活動,以便在使用者點選串流項目或相片 (或兩者皆有) 時呼叫,請按照下列步驟操作:

  1. 在專案的 res/xml/ 目錄中建立名為 contacts.xml 的檔案。如果您已擁有這個檔案,可以略過這個步驟。
  2. 在這個檔案中,將元素 <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">。 如果已有這個元素,可以略過這個步驟。
  3. 如要註冊您的其中一項活動,以便處理使用者點擊串流項目中的操作 在裝置的「聯絡人」應用程式中新增這項屬性 將 viewStreamItemActivity="activityclass" 新增至元素,其中 activityclass 是活動的完整類別名稱 ,且應接收來自裝置聯絡人應用程式的意圖。
  4. 要註冊一項活動,以便處理使用者按一下串流相片中的某個活動 在裝置的「聯絡人」應用程式中新增這項屬性 將 viewStreamItemPhotoActivity="activityclass" 新增至元素,其中 activityclass 是活動的完整類別名稱 ,且應接收來自裝置聯絡人應用程式的意圖。

如要進一步瞭解 <ContactsAccountType> 元素,請參閱 <ContactsAccountType>元素

傳入的意圖包含使用者按過的項目或相片內容 URI。如要為文字項目和相片建立個別活動,請在同一個檔案中使用這兩種屬性。

與您的社交網路服務互動

使用者不必離開裝置的聯絡人應用程式,即可邀請聯絡人加入您的社群網站。您可以改為讓裝置的聯絡人應用程式傳送意圖,邀請聯絡人加入其中一個活動。如要設定這項功能,請按照下列步驟操作:

  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 值是應接收意圖的活動的完整類別名稱。invite_action_label 值是「Add Connection」選單中顯示的文字字串 在裝置中開啟聯絡人應用程式。

注意:ContactsSourceContactsAccountType 的已淘汰代碼名稱。

contacts.xml 參考資料

檔案 contacts.xml 包含 XML 元素,可控制同步轉換器和應用程式與聯絡人應用程式和聯絡人供應器的互動情形。以下各節將說明這些元素。

<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
應用程式中活動的完整類別名稱,當使用者從裝置的聯絡人應用程式中選取「Add connection」時,您要啟動的活動。
inviteContactActionLabel
在「新增連線」選單中,針對 inviteContactActivity 中指定的活動顯示的文字字串。例如「追蹤我的網路」字串時,您可以使用字串資源 這個標籤的識別碼
viewContactNotifyService
應用程式中服務的完整類別名稱,應在使用者查看聯絡人時收到通知。這項通知是由裝置的聯絡人應用程式傳送,可讓應用程式將需要大量資料的作業延後執行,直到需要時再執行。舉例來說,應用程式可以讀取並顯示聯絡人的高解析度相片和最新的社群媒體動態項目,以回應這項通知。本節將進一步說明這項功能 社交串流互動
viewGroupActivity
應用程式中可顯示活動的完整類別名稱 群組資訊當使用者在裝置的聯絡人應用程式中點選群組標籤時,系統會顯示此活動的 UI。
viewGroupActionLabel
「聯絡人」應用程式顯示的 UI 控制項標籤, 使用者查看應用程式中的群組。

此屬性允許使用字串資源 ID。

viewStreamItemActivity
應用程式中,裝置所屬活動的完整類別名稱 當使用者按一下原始聯絡人的串流項目時,會啟動聯絡人應用程式。
viewStreamItemPhotoActivity
應用程式中活動的完整類別名稱,當使用者點選原始聯絡人的串流項目中的相片時,裝置的聯絡人應用程式會啟動該活動。

<ContactsDataKind> 元素

<ContactsDataKind> 元素會控制應用程式在聯絡人應用程式的使用者介面中顯示自訂資料列的方式。其語法如下:

<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 可繪資源。您可以使用這項功能向使用者指出資料來自您的服務。
android:summaryColumn
從資料列擷取的兩個值中,第一個值的資料欄名稱。這個值會顯示為這個資料列項目的第一行。第一行是 適合做為資料的摘要,但這是選用選項。其他參考資訊 android:detailColumn
android:detailColumn
從資料列擷取的第二兩個值,其資料欄名稱。這個值會顯示為這個資料列項目的第二行。另請參閱 android:summaryColumn

其他聯絡人提供者功能

除了前述各節說明的主要功能外,「聯絡人供應程式」還提供 下列是處理聯絡人資料的實用功能:

  • 聯絡人群組
  • 相片功能

聯絡人群組

聯絡人提供者可選擇將相關聯絡人的集合標記為 group 資料。如果與使用者帳戶相關聯的伺服器想要維護群組,則帳戶類型的同步處理器應在聯絡資訊提供者和伺服器之間傳輸群組資料。當使用者在 並將此聯絡人加入新群組,同步轉換介面必須新增群組 至 ContactsContract.Groups 資料表。原始聯絡人所屬的群組會使用 ContactsContract.CommonDataKinds.GroupMembership MIME 類型儲存在 ContactsContract.Data 表格中。

如果您要設計同步轉換介面,會從以下來源新增原始聯絡人資料: 連線至聯絡人供應程式,但您並未使用群組,因此必須告知 讓您選擇公開顯示資料的供應商。在使用者在裝置上新增帳戶時執行的程式碼中,更新聯絡人提供者為帳戶新增的 ContactsContract.Settings 資料列。在這一列中,將 第 Settings.UNGROUPED_VISIBLE 欄改為 1。這麼做後,聯絡人供應程式永遠都會 顯示聯絡人資料 (即使你並未使用群組)。

聯絡人相片

ContactsContract.Data 資料表會將相片儲存為 MIME 類型的列 Photo.CONTENT_ITEM_TYPE。資料列的 CONTACT_ID 欄已連結至 所屬原始聯絡人的 _ID 欄。 ContactsContract.Contacts.Photo 類別會定義 ContactsContract.Contacts 的子資料表,其中包含聯絡人主相片的相片資訊,也就是聯絡人主要原始聯絡資訊的主相片。同樣地,ContactsContract.RawContacts.DisplayPhoto 類別會定義 ContactsContract.RawContacts 的子資料表,其中包含原始聯絡人的主相片相片資訊。

ContactsContract.Contacts.PhotoContactsContract.RawContacts.DisplayPhoto 的參考文件包含擷取相片資訊的範例。沒有方便的類別可用來擷取原始聯絡人的首要縮圖,但您可以向 ContactsContract.Data 資料表傳送查詢,在原始聯絡人的 _IDPhoto.CONTENT_ITEM_TYPEIS_PRIMARY 欄中選取,找出原始聯絡人的首要相片資料列。

使用者的社交串流資料中也可能會包含相片。這些資料會儲存在 「android.provider.ContactsContract.StreamItemPhotos」資料表,瞭解詳情 請參閱「社群媒體串流相片」一節中的詳細說明。