1. 程式人生 > >Android簡訊中的Contacts類

Android簡訊中的Contacts類

 

 

 

Contact provider

      Contact provider是一個強大而又靈活的 Android 元件,用於管理裝置上有關聯絡人資料的中央儲存庫。 Contact provider是您在裝置的聯絡人應用中看到的資料來源,您也可以在自己的應用中訪問其資料,並可在裝置與線上服務之間傳送資料。 提供程式儲存有多種資料來源,由於它會試圖為每個聯絡人管理儘可能多的資料,因此造成其組織結構非常複雜。 為此,該提供程式的 API 包含豐富的協定類和介面,為資料檢索和修改提供便利。

本指南介紹下列內容:

     提供程式基本結構 如何從提供程式檢索資料 如何修改提供程式中的資料 如何編寫用於同步伺服器資料與聯絡人提供程式資料的同步介面卡。

Contact provider(聯絡人提供程式組織)


聯絡人提供程式是 Android 內容提供程式的一個元件。它保留了三種類型的聯絡人資料,每一種資料都對應提供程式提供的一個表,如圖 1 所示:
這裡寫圖片描述
圖 1. 聯絡人提供程式表結構。

這三個表通常以其協定類的名稱命名。這些類定義表所使用的內容 URI、列名稱及列值相應的常量:

ContactsContract.Contacts


表示不同聯絡人的行,基於聚合的原始聯絡人行。

ContactsContract.RawContacts 
包含聯絡人資料摘要的行,針對特定使用者帳戶和型別。

ContactsContract.Data 表
包含原始聯絡人詳細資訊(例如電子郵件地址或電話號碼)的行。

由 ContactsContract中的協定類表示的其他表是輔助表,Contact provider利用它們來管理其操作,或為裝置的聯絡人或電話應用中的特定功能提供支援。

表 1. 重要的原始聯絡人列。

列名稱 用途 備註
ACCOUNT_NAME 作為該原始聯絡人來源的帳戶型別的帳戶名稱。 例如,Google 帳戶的帳戶名稱是裝置所有者的某個 Gmail 地址 此名稱的格式專用於其帳戶型別。它不一定是電子郵件地址。
ACCOUNT_TYPE 作為該原始聯絡人來源的帳戶型別。例如,Google 帳戶的帳戶型別是 com.google。 請務必使用您擁有或控制的域的域識別符號限定您的帳戶型別。 這可以確保您的帳戶型別具有唯一性。 提供聯絡人資料的帳戶型別通常關聯有同步介面卡,用於與Contact provider進行同步。
DELETED 提供聯絡人資料的帳戶型別通常關聯有同步介面卡,用於與Contact provider進行同步。 此標誌讓Contact provider能夠在內部保留該行,直至同步介面卡能夠從伺服器刪除該行,然後再從儲存庫中最終刪除該行。

Original Contacts(原始聯絡人)


一個原始聯絡人表示來自某一帳戶型別和帳戶名稱、有關某個聯絡人的資料。 由於Contact provider允許將多個線上服務作為某一聯絡人的資料來源,因此它允許同一聯絡人對應多個原始聯絡人。 藉助支援多個原始聯絡人的特性,使用者還可以將某一聯絡人在帳戶型別相同的多個帳戶中的資料進行合併。

原始聯絡人的大部分資料並不儲存在 ContactsContract.RawContacts 表內,而是儲存在 ContactsContract.Data 表中的一行或多行內。每個資料行都有一個 Data.RAW_CONTACT_ID 列,其中包含其父級 ContactsContract.RawContacts 行的 android.provider.BaseColumns#_ID RawContacts._ID 值。

 

說明
以下是關於 ContactsContract.RawContacts 表的重要說明:

原始聯絡人的姓名並不儲存其在 ContactsContract.RawContacts 中的行內,而是儲存在 ContactsContract.Data 表的 ContactsContract.CommonDataKinds.StructuredName 行內。一個原始聯絡人在 ContactsContract.Data 表中只有一個該型別的行。 注意:要想在原始聯絡人行中使用您自己的帳戶資料,必須先在 AccountManager 中註冊帳戶。 為此,請提示使用者將帳戶型別及其帳戶名稱新增到帳戶列表。 如果您不這樣做,聯絡人提供程式將自動刪除您的原始聯絡人行。

例如,如果您想讓您的應用為您域名為 com.example.dataservice、基於 Web 的服務保留聯絡人資料,並且您的服務的使用者帳戶是 [email protected],則使用者必須先新增帳戶“型別”(com.example.dataservice) 和帳戶“名稱”([email protected]),然後您的應用才能新增原始聯絡人行。 您可以在文件中向用戶解釋這項要求,也可以提示使用者新增型別和名稱,或者同時採用這兩種措施。

Original contact data sources(原始聯絡人資料來源)

為理解原始聯絡人的工作方式,假設有一位使用者“Emily Dickinson”,她的裝置上定義了以下三個使用者帳戶:

[email protected] [email protected] Twitter 帳戶“belle_of_amherst”

該使用者已在 Accounts 設定中為全部三個帳戶啟用了 Sync Contacts。

假定 Emily Dickinson 開啟一個瀏覽器視窗,以 [email protected] 身份登入 Gmail,然後開啟 “聯絡人”,並新增“Thomas Higginson”。後來,她以 [email protected] 身份登入 Gmail,並向“Thomas Higginson”傳送一封電子郵件,此操作會自動將他新增為聯絡人。 她還在 Twitter 上關注了“colonel_tom”(Thomas Higginson 的 Twitter ID)。

以上操作的結果是,聯絡人提供程式會建立以下這三個原始聯絡人:

第一個原始聯絡人對應“Thomas Higginson”,關聯帳戶 [email protected]。 使用者帳戶型別是 Google。 第二個原始聯絡人對應“Thomas Higginson”,關聯帳戶 [email protected]。 使用者帳戶型別也是 Google。由於新增的聯絡人對應的使用者帳戶不同,因此儘管名稱與前一名稱完全相同,也只能作為第二個原始聯絡人。 第三個原始聯絡人對應“Thomas Higginson”,關聯帳戶“belle_of_amherst”。使用者帳戶型別是 Twitter。

Data(資料)


如前文所做的說明,原始聯絡人的資料儲存在一個 ContactsContract.Data 行中,該行連結到原始聯絡人的 _ID 值。這使一位原始聯絡人可以擁有多個具有相同資料型別的例項,例如電子郵件地址或電話號碼。 例如,如果對應 [email protected] 的“Thomas Higginson”(關聯 Google 帳戶 [email protected] 的 Thomas Higginson 的原始聯絡人行)的住宅電子郵件地址為 [email protected],辦公電子郵件地址為 [email protected],則聯絡人提供程式會儲存這兩個電子郵件地址行,並將它們都連結到原始聯絡人。

請注意,這個表中儲存了不同型別的資料。顯示姓名、電話號碼、電子郵件、郵政地址、照片以及網站明細行都可以在 ContactsContract.Data 表中找到。 為便於管理這些資料, ContactsContract.Data 表為一些列使用了描述性名稱,為其他列使用了通用名稱。 使用描述性名稱的列的內容具有相同的含義,與行中資料的型別無關,而使用通用名稱的列的內容則會隨資料型別的不同而具有不同的含義。

Descriptive Column Name(描述性列名稱)

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

RAW_CONTACT_ID
該資料對應的原始聯絡人 _ID 列的值。 MIMETYPE
該行中儲存的資料型別,以自定義 MIME(多用途網際網路郵件擴充套件)型別表示。聯絡人提供程式使用了 ContactsContract.CommonDataKinds 子類中定義的 MIME 型別。 這些 MIME 型別為開源型別,可供與聯絡人提供程式協作的任何應用或同步介面卡使用。 IS_PRIMARY
如果一個原始聯絡人可能具有多個這種型別的資料行, IS_PRIMARY 列會標記 包含該型別主要資料的資料行。例如,如果使用者長按某個聯絡人的電話號碼,並選擇 Set default,則包含該號碼的 ContactsContract.Data 行會將其 IS_PRIMARY 列設定為一個非零值。

General Column Name(通用列名稱)

有 15 個通用列命名為 DATA1 至 DATA15,可普遍適用;還有四個通用列命名為 SYNC1 至 SYNC4,只應由同步介面卡使用。 通用列名稱常量始終有效,與行包含的資料型別無關。

DATA1 列為索引列。聯絡人提供程式總是在此列中儲存其預期會成為最頻繁查詢目標的資料。 例如,在一個電子郵件行中,此列包含實際電子郵件地址。

按照慣例,DATA15 為預留列,用於儲存照片縮圖等二進位制大型物件 (BLOB) 資料。

Type-specific column names(型別專用列名稱)

為便於處理特定型別行的列,Contact provider還提供了 ContactsContract.CommonDataKinds 子類中定義的型別專用列名稱常量。 這些常量只是為同一列名稱提供不同的常量名稱,這有助於您訪問特定型別行中的資料。

例如,ContactsContract.CommonDataKinds.Email 類為 ContactsContract.Data 行定義型別專用列名稱常量,該行的 MIME 型別為 Email.CONTENT_ITEM_TYPE。 該類包含電子郵件地址列的 ADDRESS 常量。 ADDRESS 的實際值為“data1”,這與列的通用名稱相同。

注意:請勿使用具有提供程式某個預定義 MIME 型別的行向 ContactsContract.Data 表中新增您自己的自定義資料。 否則您可能會丟失資料,或導致提供程式發生故障。 例如,如果某一行具有 MIME 型別 Email.CONTENT_ITEM_TYPE,並且 DATA1 列包含的是使用者名稱而不是電子郵件地址,您就不應新增該行。如果您為該行使用自定義的 MIME 型別,則可自由定義您的自定義型別專用的列名稱,並隨心所欲地使用這些列。

圖 2 顯示的是描述性列和資料列在 ContactsContract.Data 行中的顯示情況,以及型別專用列名稱“覆蓋”通用列名稱的情況
這裡寫圖片描述
圖 2. 型別專用列名稱和通用列名稱。

Type the name of the class dedicated column(型別專用列名稱類)

表 2 列出了最常用的型別專用列名稱類:

表 2. 型別專用列名稱類

對映類 資料型別 備註
ContactsContract.CommonDataKinds.StructuredName 與該資料行關聯的原始聯絡人的姓名資料。 一位原始聯絡人只有其中一行。
ContactsContract.CommonDataKinds.Photo 與該資料行關聯的原始聯絡人的主要照片。 一位原始聯絡人只有其中一行。
ContactsContract.CommonDataKinds.Email 與該資料行關聯的原始聯絡人的電子郵件地址。 一位原始聯絡人可有多個電子郵件地址。
ContactsContract.CommonDataKinds.StructuredPostal 與該資料行關聯的原始聯絡人的郵政地址。 一位原始聯絡人可有多個郵政地址。
ContactsContract.CommonDataKinds.GroupMembership 將原始聯絡人鏈接到Contact provider內其中一組的識別符號。 組是帳戶型別和帳戶名稱的一項可選功能。

Contacts(聯絡人)

Contact provider通過將所有帳戶型別和帳戶名稱的原始聯絡人行合併來形成聯絡人。 這可以為顯示和修改使用者針對某一聯絡人收集的所有資料提供便利。 聯絡人提供程式管理新聯絡人行的建立,以及原始聯絡人與現有聯絡人行的合併。 系統不允許應用或同步介面卡新增聯絡人,並且聯絡人行中的某些列是隻讀列。

注:如果您試圖通過 insert() 向聯絡人提供程式新增聯絡人,會引發一個 UnsupportedOperationException 異常。 如果您試圖更新一個列為“只讀”的列,更新會被忽略。

如果新增的新原始聯絡人不匹配任何現有聯絡人,Contact provider會相應地建立新聯絡人。 如果某個現有原始聯絡人的資料發生了變化,不再匹配其之前關聯的聯絡人,則提供程式也會執行此操作。 如果應用或同步介面卡建立的新原始聯絡人“的確”匹配某位現有聯絡人,則新原始聯絡人將與現有聯絡人合併。

Contact provider通過 Contacts 表中聯絡人行的 _ID 列將聯絡人行與其各原始聯絡人行連結起來。 原始聯絡人表 ContactsContract.RawContacts 的 CONTACT_ID 列包含對應於每個原始聯絡人行所關聯聯絡人行的 _ID 值。

ContactsContract.Contacts 表還有一個 android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY 列,它是一個指向聯絡人行的“永久性”連結。 由於聯絡人提供程式會自動維護聯絡人,因此可能會在合併或同步時相應地更改聯絡人行的 android.provider.BaseColumns#_ID 值。 即使發生這種情況,合併了聯絡人 android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY 的內容 URI CONTENT_LOOKUP_URI 仍將指向聯絡人行,這樣,您就能使用 android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY 保持指向“最喜愛”聯絡人的連結,以及執行其他操作。 該列具有其自己的格式,與 android.provider.BaseColumns#_ID 列的格式無關。

圖 3 顯示的是這三個主要表的相互關係。
這裡寫圖片描述
圖 3. 聯絡人表、原始聯絡人表與詳細資訊表之間的關係。

Data from the synchronization adapter(來自同步介面卡的資料)


雖然使用者是直接將聯絡人資料輸入到裝置中,但這些資料也會通過同步介面卡從 Web 服務流入聯絡人提供程式中,這些同步介面卡可自動化裝置與服務之間的資料傳送。 同步介面卡在系統控制下在後臺執行,它們會呼叫 ContentResolver 方法來管理資料。

在 Android 中,與同步介面卡協作的 Web 服務通過帳戶型別加以標識。 每個同步介面卡都與一個帳戶型別協作,但它可以支援該型別的多個帳戶名稱。 原始聯絡人資料來源部分對帳戶型別和帳戶名稱做了簡要描述。 下列定義提供了更多詳細資訊,並描述了帳戶型別及帳戶名稱與同步介面卡及服務之間的關係。

帳戶型別
表示使用者在其中儲存資料的服務。在大多數時候,使用者需要向服務驗證身份。 例如,Google Contacts 是一個以程式碼 google.com 標識的帳戶型別。 該值對應於 AccountManager 使用的帳戶型別。

帳戶名稱
**表示某個帳戶型別的特定帳戶或登入名。**Google Contacts 帳戶與 Google 帳戶相同,都是以電子郵件地址作為帳戶名稱。 其他服務可能使用一個單詞的使用者名稱或數字 ID。

帳戶型別不必具有唯一性。使用者可以配置多個 Google Contacts 帳戶並將它們的資料下載到聯絡人提供程式;如果使用者為個人帳戶名稱和工作帳戶名稱分別設定了一組聯絡人,就可能發生這種情況。 帳戶名稱通常具有唯一性。 它們共同標識聯絡人提供程式與外部服務之間的特定資料流。

如果您想將服務的資料傳送到聯絡人提供程式,則需編寫您自己的同步介面卡。

圖 4 顯示的是聯絡人提供程式如何融入聯絡人資料的流動。 在名為“同步介面卡”的方框中,每個介面卡都以其帳戶型別命名。
這裡寫圖片描述
圖 4. 聯絡人提供程式資料流。

The Required Permissions(所需許可權)


想要訪問Contact provider的應用必須請求以下許可權:

對一個或多個表的讀取許可權
READ_CONTACTS,在 AndroidManifest.xml 中指定,使用 元素作為 。 對一個或多個表的寫入許可權
WRITE_CONTACTS,在 AndroidManifest.xml 中指定,使用 元素作為 。
這些許可權不適用於使用者個人資料資料。

請切記,使用者的聯絡人資料屬於個人敏感資料。使用者關心其隱私權,因此不希望應用收集有關其自身的資料或其聯絡人的資料。 如需許可權來訪問其聯絡人資料的理由並不充分,使用者可能給您的應用作出差評或乾脆拒絕安裝。

User Profile(使用者個人資料)


ContactsContract.Contacts 表有一行包含裝置使用者的個人資料資料。 這些資料描述裝置的 user 而不是使用者的其中一位聯絡人。 對於每個使用個人資料的系統,該個人資料聯絡人行都連結到某個原始聯絡人行。 每個個人資料原始聯絡人行可具有多個數據行。ContactsContract.Profile 類中提供了用於訪問使用者個人資料的常量。

訪問使用者個人資料需要特殊許可權。除了進行讀取和寫入所需的 READ_CONTACTS 和 WRITE_CONTACTS 許可權外,如果想訪問使用者個人資料,還分別需要 android.Manifest.permission#READ_PROFILE 和 android.Manifest.permission#WRITE_PROFILE 許可權進行讀取和寫入訪問。

請切記,您應該將使用者的個人資料視為敏感資料android.Manifest.permission#READ_PROFILE 許可權讓您可以訪問裝置使用者的個人身份識別資料。 請務必在您的應用的描述中告知使用者您需要使用者個人資料訪問許可權的原因。

要檢索包含使用者個人資料的聯絡人行,請呼叫 ContentResolver.query()。 將內容 URI 設定為 CONTENT_URI 並且不要提供任何選擇條件。 您還可以使用該內容 URI 作為檢索原始聯絡人或個人資料資料的基本 URI。 例如,以下程式碼段用於檢索個人資料資料:

// Sets the columns to retrieve for the user profile

mProjection = new String[]

    {

        Profile._ID,

        Profile.DISPLAY_NAME_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”。

 

(Contacts Provide Metadata)Contacts Provide元資料


Contact Provide管理用於追蹤儲存庫中聯絡人資料狀態的資料。 這些有關儲存庫的元資料儲存在各處,其中包括原始聯絡人錶行、資料錶行和聯絡人錶行、 ContactsContract.Settings 表以及 ContactsContract.SyncState 表。 下表顯示的是每一部分元資料的作用:

表 3. 聯絡人提供程式中的元資料

含義
ContactsContract.RawContacts DIRTY “0”:上次同步以來未發生變化。“1”:上次同步以來發生了變化,需要同步回伺服器。 標記裝置上因發生變化而需要同步回伺服器的原始聯絡人。 當 Android 應用更新行時,聯絡人提供程式會自動設定該值。修改原始聯絡人表或資料表的同步介面卡應始終向他們使用的內容 URI 追加字串 CALLER_IS_SYNCADAPTER。 這可以防止提供程式將行標記為已更新。 否則,即使伺服器是修改的來源,同步介面卡修改仍顯示為本地修改,並會發送到伺服器。
ContactsContract.RawContacts VERSION 此行的版本號。 每當行或其相關資料發生變化時,聯絡人提供程式都會自動增加此值。
ContactsContract.Data DATA_VERSION 此行的版本號。 每當資料行發生變化時,聯絡人提供程式都會自動增加此值。
ContactsContract.RawContacts SOURCE_ID 一個字串值,用於在建立此原始聯絡人的帳戶中對該聯絡人進行唯一標識。 當同步介面卡建立新原始聯絡人時,此列應設定為該原始聯絡人在伺服器中的唯一 ID。 當 Android 應用建立新原始聯絡人時,應將此列留空。 這是為了向同步介面卡表明,它應該在伺服器上建立新原始聯絡人,並獲取 SOURCE_ID 的值。具體地講,對於每個帳戶型別,該源 ID 都必須是唯一的,並且應在所有同步中保持穩定: 1. 唯一:帳戶的每個原始聯絡人都必須有自己的源 ID。如果您不強制執行此要求,會在聯絡人應用中引發問題。請注意,帳戶型別相同的兩個原始聯絡人可以具有相同的源 ID。 例如,允許帳戶 [email protected]com 的原始聯絡人“Thomas Higginson”與帳戶 [email protected] 的原始聯絡人“ThomasHigginson”具有相同的源 ID。 2.穩定:源 ID 是該原始聯絡人在線上服務中的資料的永久性組成部分。 例如,如果使用者從應用設定中清除儲存的聯絡人資料並重新同步,則恢復的原始聯絡人的源 ID 應與以前相同。 如果您不強制執行此要求,快捷方式將停止工作。
ContactsContract.Groups GROUP_VISIBLE “0”:此組中的聯絡人在 Android 應用 UI 中不應處於可見狀態。“1”:系統允許此組中的聯絡人在應用 UI 中處於可見狀態。 此列用於相容那些允許使用者隱藏特定組中聯絡人的伺服器。
ContactsContract.Settings UNGROUPED_VISIBLE “0”:對於此帳戶和帳戶型別,未歸入組的聯絡人在 Android 應用 UI 中處於不可見狀態。“1”:對於此帳戶和帳戶型別,未歸入組的聯絡人在應用 UI 中處於可見狀態。 預設情況下,如果聯絡人的所有原始聯絡人都未歸入組,則它們將處於不可見狀態(原始聯絡人的組成員身份通過 ContactsContract.Data 表中的一個或多個 ContactsContract.CommonDataKinds.GroupMembership 行指示)。 通過在 ContactsContract.Settings 錶行中為帳戶型別和帳戶設定此標誌,您可以強制未歸入組的聯絡人處於可見狀態。 此標誌的一個用途是顯示不使用組的伺服器上的聯絡人。
ContactsContract.SyncState (所有列) 此表用於儲存同步介面卡的元資料。 利用此表,您可以將同步狀態及其他同步相關資料持久地儲存在裝置中。

Contacts Access Provider(聯絡人提供程式訪問)


本節描述訪問聯絡人提供程式中資料的準則,側重於闡述以下內容:

實體查詢。 批量修改。 通過 Intent 執行檢索和修改。 資料完整性。

Discover Entity(查詢實體)

由於Contact Provider表是以層級形式組織,因此對於檢索某一行以及與其連結的所有“子”行,往往很有幫助。 例如,要想顯示某位聯絡人的所有資訊,您可能需要檢索某個 ContactsContract.Contacts 行的所有ContactsContract.RawContacts 行,或者檢索某個 ContactsContract.RawContacts 行的所有 ContactsContract.CommonDataKinds.Email 行。 為便於執行此操作,Contact Provider提供了實體構造,其作用類似於表間的資料庫連線。

實體類似於一個表,由父表及其子表中的選定列組成。 當您查詢實體時,需要根據實體中的可用列提供投影和搜尋條件。 結果會得到一個 Cursor,檢索的每個子錶行在其中都有一行與之對應。例如,如果您在 ContactsContract.Contacts.Entity 中查詢某個聯絡人姓名以及該姓名所有原始聯絡人的所有 ContactsContract.CommonDataKinds.Email 行,您會獲得一個 Cursor,每個 ContactsContract.CommonDataKinds.Email 行在其中都有一行與之對應。

實體簡化了查詢。使用實體時,您可以一次性檢索聯絡人或原始聯絡人的所有聯絡人資料,而不必先通過查詢父表獲得ID,然後通過該 ID 查詢子表。此外,聯絡人提供程式可通過單一事務處理實體查詢,這確保了所檢索資料的內部一致性。

注:實體通常不包含父表和子表的所有列。 如果您試圖使用的列名稱並未出現在實體的列名稱常量列表中,則會引發一個 Exception。

以下程式碼段說明如何檢索某位聯絡人的所有原始聯絡人行。該程式碼段是一個大型應用的組成部分,包含“主”和“詳”兩個 Activity。 主 Activity 顯示一個聯絡人行列表;當用戶選擇一行時,該 Activity 會將其 ID 傳送至詳 Activity。 詳 Activity 使用 ContactsContract.Contacts.Entity 顯示與所選聯絡人關聯的所有原始聯絡人中的所有資料行。

/*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    mContactUri = Uri.withAppendedPath(
            mContactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);
 
    // Initializes the loader identified by LOADER_ID.
    getLoaderManager().initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this);      // The context of the activity
 
    // Creates a new cursor adapter to attach to the list view
    mCursorAdapter = new SimpleCursorAdapter(
            this,                        // the context of the activity
            R.layout.detail_list_item,   // the view item containing the detail widgets
            mCursor,                     // the backing cursor
            mFromColumns,                // the columns in the cursor that provide the data
            mToViews,                    // the views in the view item that display the data
            0);                          // flags
 
    // Sets the ListView's backing adapter.
    mRawContactList.setAdapter(mCursorAdapter);
...
@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
            mContactUri,              // The entity content URI for a single contact
            projection,               // The columns to retrieve
            null,                     // Retrieve all the raw contacts and their data rows.
            null,                     //
            sortOrder);               // Sort by the raw contact ID.
}