Android官方文档—APP组件(Content Providers)(Contacts Provider)
通訊錄內容提供者
Contacts Provider是一個功能強大且靈活的Android組件,用于管理設備的人員數據中??央存儲庫。聯系人提供程序是您在設備的聯系人應用程序中看到的數據源,您還可以在自己的應用程序中訪問其數據,并在設備和在線服務之間傳輸數據。提供商可以容納各種數據源,并嘗試為每個人管理盡可能多的數據,結果是組織結構復雜。因此,提供者的API包括一組廣泛的合同類和接口,便于數據檢索和修改。
本指南介紹以下內容:
- 基本的提供者結構。
- 如何從提供程序檢索數據。
- 如何修改提供程序中的數據。
- 如何編寫同步適配器以將數據從服務器同步到Contacts Provider。
本指南假設您了解Android內容提供商的基礎知識。要了解有關Android內容提供商的更多信息,請閱讀內容提供商基礎知識指南。 Sample Sync Adapter示例應用程序是使用同步適配器在Contacts Provider和Google Web Services托管的示例應用程序之間傳輸數據的示例。
通訊錄供應商架構
Contacts Provider是Android內容提供商組件。它維護有關一個人的三種類型的數據,每種數據對應于提供者提供的表,如圖1所示:
圖1. Contacts Provider表結構。這三個表通常用其合同類的名稱來表示。這些類定義了表使用的內容URI,列名和列值的常量:
ContactsContract.Contacts表
代表不同人的行,基于原始聯系行的聚合。
ContactsContract.RawContacts表
包含人員數據摘要的行,特定于用戶帳戶和類型。
ContactsContract.Data表
行包含原始聯系人的詳細信息,例如電子郵件地址或電話號碼。
ContactsContract中由合同類表示的其他表是Contacts Provider用于管理其操作或支持設備的聯系人或電話應用程序中的特定功能的輔助表。
原始聯系人
原始聯系人表示來自單個帳戶類型和帳戶名稱的人員數據。由于聯系人提供程序允許多個聯機服務作為人員的數據源,因此聯系人提供程序允許同一個人使用多個原始聯系人。多個原始聯系人還允許用戶組合來自同一帳戶類型的多個帳戶的人員數據。
原始聯系人的大多數數據不存儲在ContactsContract.RawContacts表中。相反,它存儲在ContactsContract.Data表中的一行或多行中。每個數據行都有一列Data.RAW_CONTACT_ID,其中包含其父ContactsContract.RawContacts行的RawContacts._ID值。
重要的原始數據列
ContactsContract.RawContacts表中的重要列列在表1中。請閱讀表后面的注釋:
表1.重要的原始接觸列。
| 列名 | 用法 | 備注 |
| ACCOUNT_NAME | 作為此原始聯系人來源的帳戶類型的帳戶名稱。例如,Google帳戶的帳戶名稱是設備所有者的Gmail地址之一。有關詳細信息,請參閱ACCOUNT_TYPE的下一個條目。 | 此名稱的格式特定于其帳戶類型。它不一定是電子郵件地址。 |
| ACCOUNT_TYPE | 作為此原始聯系人來源的帳戶類型。例如,Google帳戶的帳戶類型是com.google。始終使用您擁有或控制的域的域標識符限定您的帳戶類型。這將確保您的帳戶類型是唯一的。 | 提供聯系人數據的帳戶類型通常具有與聯系人提供程序同步的關聯同步適配器。 |
| DELETED | 原始聯系人的“已刪除”標志。 | 此標志允許聯系人提供程序在內部維護該行,直到同步適配器能夠從其服務器中刪除該行,然后最終從存儲庫中刪除該行。 |
注意
以下是有關ContactsContract.RawContacts表的重要說明:
- 原始聯系人的姓名未存儲在ContactsContract.RawContacts的行中。相反,它存儲在ContactsContract.CommonDataKinds.StructuredName行的ContactsContract.Data表中。原始聯系人在ContactsContract.Data表中只有一行此類型。
- 警告:要在原始聯系人行中使用您自己的帳戶數據,必須先在AccountManager中注冊。為此,請提示用戶將帳戶類型及其帳戶名稱添加到帳戶列表中。如果您不這樣做,聯系人提供程序將自動刪除您的原始聯系人行。
例如,如果您希望自己的應用使用域com.example.dataservice維護基于Web的服務的聯系人數據,并且用戶的服務帳戶是becky.sharp@dataservice.example.com,則用戶必須先添加在您的應用添加原始聯系人行之前,帳戶“type”(com.example.dataservice)和帳戶“name”(becky.smart@dataservice.example.com)。您可以在文檔中向用戶解釋此要求,也可以提示用戶添加類型和名稱,或兩者都添加。帳戶類型和帳戶名稱將在下一節中詳細介紹。
原始聯系人數據的來源
要了解原始聯系人的工作原理,請考慮在其設備上定義了以下三個用戶帳戶的用戶“Emily Dickinson”:
- emily.dickinson@gmail.com
- emilyd@gmail.com
- Twitter account "belle_of_amherst"
此用戶已在“帳戶”設置中為所有這三個帳戶啟用了“同步聯系人”。
假設Emily Dickinson打開瀏覽器窗口,以emily.dickinson@gmail.com登錄Gmail,打開聯系人,并添加“Thomas Higginson”。稍后,她以emilyd@gmail.com身份登錄Gmail,并向“Thomas Higginson”發送電子郵件,自動將其添加為聯系人。她還在Twitter上關注“colonel_tom”(Thomas Higginson的Twitter ID)。
由于這項工作,Contacts Provider會創建三個原始聯系人:
數據
如前所述,原始聯系人的數據存儲在ContactsContract.Data行中,該行鏈接到原始聯系人的_ID值。這允許單個原始聯系人具有相同類型數據的多個實例,例如電子郵件地址或電話號碼。例如,如果emilyd@gmail.com的“Thomas Higginson”(Thomas Higginson的原始聯系人行與Google帳戶emilyd@gmail.com相關聯)的家庭電子郵件地址為thigg@gmail.com,工作電子郵件地址為thomas.higginson@gmail.com,Contacts Provider存儲兩個電子郵件地址行,并將它們鏈接到原始聯系人。
請注意,此單個表中存儲了不同類型的數據。顯示名稱,電話號碼,電子郵件,郵政地址,照片和網站詳細信息行均可在ContactsContract.Data表中找到。為了幫助管理它,ContactsContract.Data表包含一些具有描述性名稱的列,以及具有通用名稱的其他列。無論行中的數據類型如何,描述性名稱列的內容都具有相同的含義,而通用名稱列的內容根據數據類型具有不同的含義。
描述性列名
描述性列名的一些示例是:
RAW_CONTACT_ID
此數據的原始聯系人的_ID列的值。
MIMETYPE
存儲在此行中的數據類型,表示為自定義MIME類型。 Contacts Provider使用ContactsContract.CommonDataKinds子類中定義的MIME類型。這些MIME類型是開源的,可以與任何與Contacts Provider一起使用的應用程序或同步適配器使用。
IS_PRIMARY
如果原始聯系人可以多次出現此類數據行,則IS_PRIMARY列會標記包含該類型主數據的數據行。例如,如果用戶長按聯系人的電話號碼并選擇“設置默認值”,則包含該號碼的ContactsContract.Data行將其IS_PRIMARY列設置為非零值。
通用列名稱
有15個名為DATA1到DATA15的通用列通常可用,還有另外四個通用列SYNC1到SYNC4,它們只能由同步適配器使用。無論行包含哪種數據類型,通用列名常量始終有效。
DATA1列已編制索引。聯系人提供程序始終將此列用于提供程序期望的數據,該數據將是查詢的最常見目標。例如,在電子郵件行中,此列包含實際的電子郵件地址。
按照慣例,列DATA15被保留用于存儲二進制大對象(BLOB)數據,例如照片縮略圖。
特定于類型的列名稱
為了便于使用特定類型行的列,Contacts Provider還提供了在ContactsContract.CommonDataKinds的子類中定義的特定于類型的列名常量。常量只是為同一列名提供不同的常量名稱,這有助于您訪問特定類型的行中的數據。
例如,ContactsContract.CommonDataKinds.Email類為ContactsContract.Data行定義類型特定的列名常量,該行具有MIME類型Email.CONTENT_ITEM_TYPE。該類包含電子郵件地址列的常量ADDRESS。 ADDRESS的實際值是“data1”,它與列的通用名稱相同。
警告:不要使用具有提供程序的預定義MIME類型之一的行將自己的自定義數據添加到ContactsContract.Data表。如果這樣做,您可能會丟失數據或導致提供商出現故障。例如,您不應在DATA1列中添加包含用戶名而不是電子郵件地址的MIME類型Email.CONTENT_ITEM_TYPE的行。如果您對該行使用自己的自定義MIME類型,則可以自由定義自己的特定于類型的列名稱,并根據需要使用列。
圖2顯示了描述性列和數據列在ContactsContract.Data行中的顯示方式,以及特定于類型的列名稱如何“覆蓋”通用列名稱。
圖2.特定于類型的列名和通用列名。特定于類型的列名稱類
表2列出了最常用的特定于類型的列名類:
表2.特定于類型的列名類
| 映射類 | 數據類型 | 備注 |
| ContactsContract.CommonDataKinds.StructuredName | 與此數據行關聯的原始聯系人的名稱數據。 | 原始聯系人只有這些行中的一行。 |
| ContactsContract.CommonDataKinds.Photo | 與此數據行關聯的原始聯系人的主照片。 | 原始聯系人只有這些行中的一行。 |
| ContactsContract.CommonDataKinds.Email | 與此數據行關聯的原始聯系人的電子郵件地址。 | 原始聯系人可以有多個電子郵件地址。 |
| ContactsContract.CommonDataKinds.StructuredPostal | 與此數據行關聯的原始聯系人的郵政地址。 | 原始聯系人可以有多個郵政地址。 |
| ContactsContract.CommonDataKinds.GroupMembership | 將原始聯系人鏈接到“聯系人”提供程序中的一個組的標識符。 | 組是帳戶類型和帳戶名稱的可選功能。它們在聯系人組部分中有更詳細的描述。 |
通訊錄
聯系人提供商將所有帳戶類型和帳戶名稱中的原始聯系人行組合在一起以形成聯系人。這有助于顯示和修改用戶為人收集的所有數據。聯系人提供程序管理新聯系人行的創建,以及原始聯系人與現有聯系人行的聚合。應用程序和同步適配器都不允許添加聯系人,聯系行中的某些列是只讀的。
注意:如果您嘗試使用insert()將聯系人添加到Contacts Provider,則會收到UnsupportedOperationException異常。如果您嘗試更新列為“只讀”的列,則忽略更新。
聯系人提供商創建新聯系人以響應添加與任何現有聯系人不匹配的新原始聯系人。如果現有原始聯系人的數據以不再與之前附加的聯系人匹配的方式更改,則提供程序也會執行此操作。如果應用程序或同步適配器創建與現有聯系人匹配的新原始聯系人,則新的原始聯系人將聚合到現有聯系人。
“聯系人”提供程序將聯系人行與其原始聯系人行鏈接,并在“聯系人”表中將聯系人行的_ID列鏈接。原始聯系人表ContactsContract.RawContacts的CONTACT_ID列包含與每個原始聯系人行關聯的聯系人行的_ID值。
ContactsContract.Contacts表還有LOOKUP_KEY列,它是聯系行的“永久”鏈接。由于聯系人提供程序會自動維護聯系人,因此可能會更改聯系人行的_ID值以響應聚合或同步。即使發生這種情況,內容URI CONTENT_LOOKUP_URI結合聯系人的LOOKUP_KEY仍將指向聯系人行,因此您可以使用LOOKUP_KEY維護指向“最喜歡”聯系人的鏈接,依此類推。此列具有自己的格式,與_ID列的格式無關。
圖3顯示了三個主要表格如何相互關聯。
圖3. Contacts,Raw Contacts和Details表關系。來自同步適配器的數據
用戶將聯系人數據直接輸入設備,但數據也通過同步適配器從Web服務流入聯系人提供程序,這樣可以自動在設備和服務之間傳輸數據。同步適配器在系統的控制下在后臺運行,并且它們調用ContentResolver方法來管理數據。
在Android中,同步適配器使用的Web服務由帳戶類型標識。每個同步適配器使用一種帳戶類型,但它可以支持該類型的多個帳戶名稱。帳戶類型和帳戶名稱在原始聯系人數據源中簡要描述。以下定義提供了更多詳細信息,并描述了帳戶類型和名稱與同步適配器和服務的關系。
帳戶類型
標識用戶已存儲數據的服務。大多數情況下,用戶必須使用該服務進行身份驗證。例如,Google通訊錄是一種帳戶類型,由代碼google.com標識。此值對應于AccountManager使用的帳戶類型。
用戶名
標識帳戶類型的特定帳戶或登錄。 Google通訊錄帳戶與Google帳戶相同,后者的電子郵件地址為帳戶名稱。其他服務可能使用單字用戶名或數字ID。
帳戶類型不必是唯一的。用戶可以配置多個Google通訊錄帳戶并將其數據下載到聯系人提供商;如果用戶有一組個人帳戶名稱的個人聯系人,另一組用于工作,則可能發生這種情況。帳戶名稱通常是唯一的。它們共同確定了聯系人提供者和外部服務之間的特定數據流。
如果要將服務的數據傳輸到Contacts Provider,則需要編寫自己的同步適配器。 “聯系人提供程序同步適配器”一節中對此進行了更詳細的描述。
圖4顯示了Contacts Provider如何適應人員數據流。在標有“同步適配器”的框中,每個適配器都標有其帳戶類型。
圖4. Contacts Provider數據流。必需的權限
想要訪問聯系人提供程序的應用程序必須請求以下權限:
對一個或多個表的讀訪問權限
READ_CONTACTS,在AndroidManifest.xml中指定,<uses-permission>元素為<uses-permission android:name =“android.permission.READ_CONTACTS”>。
對一個或多個表的寫訪問權
WRITE_CONTACTS,在AndroidManifest.xml中指定,<uses-permission>元素為<uses-permission android:name =“android.permission.WRITE_CONTACTS”>。
這些權限不會擴展到用戶配置文件數據。用戶配置文件及其所需權限將在下一節“用戶配置文件”中討論。
請記住,用戶的聯系人數據是個人且敏感的。用戶擔心他們的隱私,因此他們不希望應用程序收集有關他們或他們的聯系人的數據。如果您需要獲得訪問其聯系人數據的權限并不明顯,則可能會使您的應用程序評級較低,或者只是拒絕安裝它。
用戶檔案
ContactsContract.Contacts表有一行包含設備用戶的配置文件數據。此數據描述設備的用戶而不是用戶的一個聯系人。配置文件聯系人行鏈接到使用配置文件的每個系統的原始聯系人行。每個配置文件原始聯系人行可以有多個數據行。 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”。
聯系人提供商元數據
聯系人提供程序管理跟蹤存儲庫中聯系人數據狀態的數據。有關存儲庫的此元數據存儲在各種位置,包括Raw Contacts,Data和Contacts表行,ContactsContract.Settings表和ContactsContract.SyncState表。下表顯示了每個元數據的效果:
表3. Contacts Provider中的元數據
| 表 | 列 | 值 | 意義 |
| ContactsContract.RawContacts | DIRTY | “0” - 自上次同步后未更改。 | 標記在設備上更改的原始聯系人,并且必須同步回服務器。當Android應用程序更新行時,聯系人提供程序會自動設置該值。 |
| “1” - 自上次同步后更改,需要同步回服務器。 | 修改原始聯系人或數據表的同步適配器應始終將字符串CALLER_IS_SYNCADAPTER附加到他們使用的內容URI。這可以防止提供程序將行標記為臟。否則,同步適配器修改似乎是本地修改并發送到服務器,即使服務器是修改的源。 | ||
| ContactsContract.RawContacts | VERSION | 此行的版本號。 | 每當行或其相關數據發生更改時,Contacts Provider會自動遞增此值。 |
| ContactsContract.Data | DATA_VERSION | 此行的版本號。 | 每當更改數據行時,Contacts Provider都會自動遞增此值。 |
| ContactsContract.RawContacts | SOURCE_ID | 一個字符串值,用于唯一標識與創建該聯系人的帳戶的原始聯系人。 | 當同步適配器創建新的原始聯系人時,應將此列設置為服務器的原始聯系人的唯一ID。當Android應用程序創建新的原始聯系人時,應用程序應將此列留空。這表示同步適配器應在服務器上創建新的原始聯系人,并獲取SOURCE_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 | (all) | 使用此表存儲同步適配器的元數據。 | 使用此表,您可以在設備上持久存儲同步狀態和其他與同步相關的數據。 |
聯系提供者訪問
本節介紹從“聯系人提供程序”訪問數據的準則,重點介紹以下內容:
- 實體查詢。
- 批量修改。
- 使用意圖進行檢索和修改。
- 數據的完整性。
“聯系人提供程序同步適配器”一節中還詳細介紹了如何從同步適配器進行修改。
查詢實體
由于Contacts Provider表是按層次結構組織的,因此檢索行和鏈接到它的所有“子”行通常很有用。例如,要顯示人員的所有信息,您可能需要檢索單個ContactsContract.Contacts行的所有ContactsContract.RawContacts行,或單個ContactsContract.RawContacts行的所有ContactsContract.CommonDataKinds.Email行。為此,Contacts Provider提供實體構造,其作用類似于表之間的數據庫連接。
實體就像一個由父表及其子表中的選定列組成的表。查詢實體時,您將根據實體中可用的列提供投影和搜索條件。結果是包含的Cursor包含檢索到的每個子表行的一行。例如,如果查詢ContactsContract.Contacts.Entity以獲取該名稱的所有原始聯系人的聯系人姓名和所有ContactsContract.CommonDataKinds.Email行,則會返回一個Cursor,其中包含每行ContactsContract.CommonDataKinds.Email行的一行。
實體簡化了查詢。使用實體,您可以立即檢索聯系人或原始聯系人的所有聯系人數據,而不必首先查詢父表以獲取ID,然后必須使用該ID查詢子表。此外,聯系人提供程序在單個事務中處理針對實體的查詢,這確保檢索到的數據在內部是一致的。
注意:實體通常不包含父表和子表的所有列。如果您嘗試使用不在實體的列名常量列表中的列名,您將獲得異常。
以下代碼段顯示了如何檢索聯系人的所有原始聯系人行。該代碼段是一個較大的應用程序的一部分,它有兩個活動,“主要”和“細節”。主要活動顯示聯系人行列表;當用戶選擇一個時,活動將其ID發送到詳細活動。詳細信息活動使用ContactsContract.Contacts.Entity顯示與所選聯系人關聯的所有原始聯系人的所有數據行。
此代碼段取自“詳細信息”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(),以“批處理模式”在Contacts Provider中插入,更新和刪除數據。由于Contacts Provider在單個事務中執行applyBatch()中的所有操作,因此您的修改永遠不會使聯系人存儲庫處于不一致狀態。批量修改還有助于同時插入原始聯系人及其詳細數據。
注意:要修改單個原始聯系人,請考慮向設備的聯系人應用程序發送意圖,而不是在應用程序中處理修改。在使用意圖檢索和修改一節中更詳細地描述了這樣做。
屈服點
包含大量操作的批量修改可能會阻止其他進程,從而導致整體用戶體驗不佳。要在盡可能少的單獨列表中組織要執行的所有修改,同時防止它們阻止系統,您應該為一個或多個操作設置屈服點。屈服點是ContentProviderOperation對象,其isYieldAllowed()值設置為true。當聯系人提供程序遇到屈服點時,它會暫停其工作以讓其他進程運行并關閉當前事務。當提供程序再次啟動時,它將繼續執行ArrayList中的下一個操作并啟動新事務。
服點確實會導致每次調用applyBatch()時有多個事務。因此,您應為一組相關行的最后一個操作設置屈服點。例如,您應該為添加原始聯系人行及其關聯數據行的集合中的最后一個操作設置屈服點,或者為與單個聯系人相關的一組行設置最后一個操作。
屈服點也是原子操作的單位。兩個屈服點之間的所有訪問將作為單個單元成功或失敗。如果未設置任何屈服點,則最小的原子操作是整批操作。如果使用屈服點,則可以防止操作降低系統性能,同時確保操作子集是原子的。
修改后引用
當您將新的原始聯系人行及其關聯的數據行作為一組ContentProviderOperation對象插入時,必須通過將原始聯系人的_ID值作為RAW_CONTACT_ID值插入,將數據行鏈接到原始聯系人行。但是,當您為數據行創建ContentProviderOperation時,此值不可用,因為您尚未為原始聯系人行應用ContentProviderOperation。要解決此問題,ContentProviderOperation.Builder類具有withValueBackReference()方法。此方法允許您插入或修改具有上一操作結果的列。
withValueBackReference()方法有兩個參數:
key
鍵值對的關鍵。此參數的值應該是您正在修改的表中列的名稱。
previousResult
來自applyBatch()的ContentProviderResult對象數組中的值的從0開始的索引。在應用批處理操作時,每個操作的結果都存儲在一個中間結果數組中。 previousResult值是這些結果之一的索引,使用鍵值檢索和存儲。這允許您插入新的原始聯系人記錄并獲取其_ID值,然后在添加ContactsContract.Data行時對該值進行“后向引用”。
第一次調用applyBatch()時會創建整個結果數組,其大小等于您提供的ContentProviderOperation對象的ArrayList的大小。但是,結果數組中的所有元素都設置為null,如果嘗試對尚未應用的操作的結果進行反向引用,則withValueBackReference()會拋出異常。
以下代碼段顯示了如何批量插入新的原始聯系人和數據。它們包括建立屈服點并使用反向引用的代碼。這些片段是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對象,該操作添加原始聯系人行并返回其新的_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行時使用樂觀并發控制,請按照下列步驟操作:
如果在您讀取行的時間和嘗試修改行的時間之間通過另一個操作更新原始聯系行,則“斷言”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}使用意圖進行檢索和修改
向設備的聯系人應用程序發送意圖允許您間接訪問聯系人提供程序。意圖啟動設備的聯系人應用程序UI,用戶可以在其中執行與聯系人相關的工作。通過這種類型的訪問,用戶可以:
- 從列表中選擇一個聯系人,并將其返回到您的應用程序以進行進一步的工作。
- 編輯現有聯系人的數據。
- 為他們的任何帳戶插入新的原始聯系人。
- 刪除聯系人或聯系人數據。
如果用戶正在插入或更新數據,您可以先收集數據并將其作為意圖的一部分發送。
當您使用意圖通過設備的聯系人應用程序訪問聯系人提供程序時,您不必編寫自己的UI或代碼來訪問提供程序。您也不必請求讀取或寫入提供程序的權限。設備的聯系人應用程序可以將聯系人的讀取權限委派給您,并且由于您通過其他應用程序對提供程序進行了修改,因此您不必具有寫入權限。
發送訪問提供程序的意圖的一般過程在“內容提供程序基礎知識”指南的“通過意圖進行數據訪問”一節中有詳細介紹。表4總結了用于可用任務的操作,MIME類型和數據值,而可以與putExtra()一起使用的extras值列在ContactsContract.Intents.Insert的參考文檔中:
表4.聯系人提供者意圖。
| Task | Action | Data | MIME type | Notes |
| 從列表中選擇一個聯系人 | ACTION_PICK | 以下之一:
| Not used | 顯示原始聯系人列表或原始聯系人的數據列表,具體取決于您提供的內容URI類型。 調用startActivityForResult(),它返回所選行的內容URI。 URI的形式是表的內容URI,其中附加了行的LOOKUP_ID。設備的聯系人應用程序在活動期間委托對此內容URI的讀寫權限。有關更多詳細信息,請參閱Content Provider Basics指南。 |
| 插入新的原始聯系人 | Insert.ACTION | N/A | RawContacts.CONTENT_TYPE,一組原始聯系人的MIME類型。 | 顯示設備的聯系人應用程序的“添加聯系人”屏幕。將顯示您添加到意圖中的額外值。如果使用startActivityForResult()發送,則新添加的原始聯系人的內容URI將在“數據”字段中的Intent參數中傳遞回活動的onActivityResult()回調方法。要獲取該值,請調用getData()。 |
| 編輯聯系人 | ACTION_EDIT | CONTENT_LOOKUP_URI為聯系人。編輯器活動將允許用戶編輯與此聯系人關聯的任何數據。 | Contacts.CONTENT_ITEM_TYPE,單個聯系人。 | 顯示聯系人應用程序中的編輯聯系人屏幕。將顯示您添加到意圖中的額外值。當用戶單擊“完成”以保存編輯時,您的活動將返回到前臺。 |
| 顯示也可以添加數據的選擇器。 | ACTION_INSERT_OR_EDIT | N/A | CONTENT_ITEM_TYPE | 此意圖始終顯示聯系人應用程序的選擇器屏幕。用戶可以選擇要編輯的聯系人,也可以添加新聯系人。根據用戶的選擇,將顯示編輯或添加屏幕,并顯示您在意圖中傳遞的額外數據。如果您的應用顯示聯系人數據(如電子郵件或電話號碼),請使用此意圖允許用戶將數據添加到現有聯系人。 注意:無需在此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.CommonDataKinds.StructuredName行的ContactsContract.RawContacts行可能會在聚合期間導致問題。
始終將新的ContactsContract.Data行鏈接到其父ContactsContract.RawContacts行。
未鏈接到ContactsContract.RawContacts的ContactsContract.Data行將在設備的聯系人應用程序中不可見,并且可能會導致同步適配器出現問題。
僅為您擁有的原始聯系人更改數據。
請記住,聯系人提供商通常管理來自多種不同帳戶類型/在線服務的數據。您需要確保應用程序僅修改或刪除屬于您的行的數據,并且僅插入具有您控制的帳戶類型和名稱的數據。
始終使用ContactsContract及其子類中定義的常量來獲取權限,內容URI,URI路徑,列名,MIME類型和TYPE值。
使用這些常量可以幫助您避免錯誤。如果不推薦任何常量,也會收到編譯器警告通知。
自定義數據行
通過創建和使用自己的自定義MIME類型,您可以在ContactsContract.Data表中插入,編輯,刪除和檢索自己的數據行。您的行僅限于使用ContactsContract.DataColumns中定義的列,但您可以將自己的特定于類型的列名稱映射到默認列名稱。在設備的聯系人應用程序中,將顯示行的數據,但無法編輯或刪除,用戶無法添加其他數據。要允許用戶修改自定義數據行,您必須在自己的應用程序中提供編輯器活動。
顯示自定義數據,請提供包含<ContactsAccountType>元素及其一個或多個<ContactsDataKind>子元素的contacts.xml文件。這在<ContactsDataKind>元素部分中有更詳細的描述。
要了解有關自定義MIME類型的更多信息,請閱讀“創建內容提供商”指南。
聯系人提供商同步適配器
聯系人提供程序專門用于處理設備和在線服務之間的聯系人數據同步。這允許用戶將現有數據下載到新設備并將現有數據上載到新帳戶。無論添加和更改的來源如何,同步還可確保用戶掌握最新數據。同步的另一個優點是,即使設備未連接到網絡,它也可以使聯系人數據可用。
雖然您可以通過多種方式實現同??步,但Android系統提供了一個插件同步框架,可自動執行以下任務:
- 檢查網絡可用性。
- 根據用戶首選項調度和執行同步。
- 重新啟動已停止的同步。
要使用此框架,請提供同步適配器插件。每個同步適配器對于服務和內容提供商都是唯一的,但可以處理同一服務的多個帳戶名。該框架還允許同一服務和提供者使用多個同步適配器。
同步適配器類和文件
您將同步適配器實現為AbstractThreadedSyncAdapter的子類,并將其安裝為Android應用程序的一部分。系統從應用程序清單中的元素以及清單指向的特殊XML文件中了解同步適配器。 XML文件定義在線服務的帳戶類型和內容提供者的權限,它們一起唯一地標識適配器。在用戶為同步適配器的帳戶類型添加帳戶并為同步適配器同步的內容提供程序啟用同步之前,同步適配器不會變為活動狀態。此時,系統開始管理適配器,根據需要調用它以在內容提供者和服務器之間進行同步。
注意:使用帳戶類型作為同步適配器標識的一部分,系統可以檢測同一組合的同步適配器并將其組合在一起。例如,Google在線服務的同步適配器都具有相同的帳戶類型com.google。當用戶將Google帳戶添加到他們的設備時,所有已安裝的Google服務同步適配器都會列在一起;列出的每個同步適配器與設備上的其他內容提供程序同步。
由于大多數服務要求用戶在訪問數據之前驗證其身份,因此Android系統提供的身份驗證框架與同步適配器框架類似,并且通常與其同時使用。身份驗證框架使用插件身份驗證器,它是AbstractAccountAuthenticator的子類。驗證者通過以下步驟驗證用戶的身份:
- 收集用戶的名稱,密碼或類似信息(用戶的憑據)。
- 將憑據發送到服務
- 檢查服務的回復。
如果服務接受憑據,則身份驗證器可以存儲憑據以供以后使用。由于插件驗證器框架,AccountManager可以提供對驗證者支持并選擇公開的任何驗證的訪問權限,例如OAuth2 authtokens。
雖然不需要身份驗證,但大多數聯系人服務都使用它。但是,您不需要使用Android身份驗證框架進行身份驗證。
同步適配器實現
要為Contacts Provider實現同步適配器,首先要創建一個包含以下內容的Android應用程序:
一個服務組件,響應來自系統的請求綁定到同步適配器。
當系統想要運行同步時,它會調用服務的onBind()方法來獲取同步適配器的IBinder。這允許系統對適配器的方法執行跨進程調用。
在Sample Sync Adapter示例應用程序中,此服務的類名是com.example.android.samplesync.syncadapter.SyncService。
實際的同步適配器,實現為AbstractThreadedSyncAdapter的具體子類。
此類負責從服務器下載數據,從設備上傳數據以及解決沖突。適配器的主要工作是在onPerformSync()方法中完成的。必須將此類實例化為單例。
在Sample Sync Adapter示例應用程序中,同步適配器在com.example.android.samplesync.syncadapter.SyncAdapter類中定義。
Application的子類。
此類充當同步適配器單例的工廠。使用onCreate()方法實例化同步適配器,并提供靜態“getter”方法以將單例返回到同步適配器服務的onBind()方法。
可選:響應來自系統的用戶身份驗證請求的服務組件。
AccountManager啟動此服務以開始身份驗證過程。服務的onCreate()方法實例化一個authenticator對象。當系統想要為應用程序的同步適配器驗證用戶帳戶時,它會調用服務的onBind()方法來獲取驗證者的IBinder。這允許系統對認證者的方法進行跨進程調用。
在Sample Sync Adapter示例應用程序中,此服務的類名是com.example.android.samplesync.authenticator.AuthenticationService。
可選:AbstractAccountAuthenticator的一個具體子類,用于處理身份驗證請求。
此類提供AccountManager調用的方法,以便使用服務器驗證用戶的憑據。根據使用的服務器技術,身份驗證過程的細節差異很大。您應參閱服務器軟件的文檔以了解有關身份驗證的更多信息。
在Sample Sync Adapter示例應用程序中,驗證程序在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#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)。
照片的二進制表示,由提供程序調整大小以進行存儲和顯示。此列可用于向后兼容用于存儲照片的以前版本的Contacts Provider。但是,在當前版本中,您不應使用此列來存儲照片。相反,使用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()以獲取照片文件的句柄。
使用社交流表
這些表的工作方式與Contacts Provider中的其他主表相同,不同之處在于:
- 這些表需要其他訪問權限。要從中讀取,您的應用程序必須具有android.Manifest.permission#READ_SOCIAL_STREAM權限。要修改它們,您的應用程序必須具有android.Manifest.permission#WRITE_SOCIAL_STREAM權限。
- 對于android.provider.ContactsContract.StreamItems表,為每個原始聯系人存儲的行數是有限的。達到此限制后,Contacts Provider會自動刪除具有最舊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的子表。
社交流互動
聯系人提供商管理的社交流數據與設備的聯系人應用程序一起,提供了一種將社交網絡系統與現有聯系人連接的強大方式。可以使用以下功能:
- 通過使用同步適配器將您的社交網絡服務同步到Contacts Provider,您可以檢索用戶聯系人的最近活動,并將其存儲在android.provider.ContactsContract.StreamItems和android.provider.ContactsContract.StreamItemPhotos表中供以后使用。
- 除了常規同步之外,您還可以在用戶選擇要查看的聯系人時觸發同步適配器以檢索其他數據。這允許您的同步適配器檢索高分辨率照片和聯系人的最新流項目。
- 通過向設備的聯系人應用程序和聯系人提供程序注冊通知,您可以在查看聯系人時收到意圖,并在此時更新服務中的聯系人狀態。與使用同步適配器進行完全同步相比,此方法可能更快并且使用更少的帶寬。
- 用戶可以在查看設備聯系人應用程序中的聯系人時為您的社交網絡服務添加聯系人。您可以使用“邀請聯系人”功能啟用此功能,該功能通過將現有聯系人添加到網絡的活動和提供設備聯系人應用程序的XML文件以及提供應用程序詳細信息的聯系人提供程序的組合啟用。
流項目與Contacts Provider的定期同步與其他同步相同。有關同步的詳細信息,請參閱“聯系人提供程序同步適配器”一節。接下來的兩節將介紹注冊通知和邀請聯系人。
注冊以處理社交網絡視圖
注冊同步適配器以在用戶查看由同步適配器管理的聯系人時接收通知:
要注冊當用戶單擊流項目或照片或兩者時要調用的活動:
<ContactsAccountType>元素在<ContactsAccountType>元素中有更詳細的描述。
傳入的意圖包含用戶單擊的項目或照片的內容URI。要為文本項和照片分別進行活動,請在同一文件中使用這兩個屬性。
與您的社交網絡服務進行交互
用戶無需離開設備的聯系人應用程序即可邀請聯系人加入您的社交網站。相反,您可以讓設備的聯系人應用程序發送邀請聯系人參與您的某項活動的意圖。要設置它:
- inviteContactActivity="activityclass"
- inviteContactActionLabel="@string/invite_action_label"
activityclass值是應該接收intent的活動的完全限定類名。 invite_action_label值是一個文本字符串,顯示在設備的聯系人應用程序的“添加連接”菜單中。
注意: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
當用戶從設備的聯系人應用程序中選擇添加連接時,要激活的應用程序中活動的完全限定類名。
inviteContactActionLabel
在“添加連接”菜單中為inviteContactActivity中指定的活動顯示的文本字符串。例如,您可以使用字符串“Follow in my network”。您可以為此標簽使用字符串資源標識符。
viewContactNotifyService
應用程序中服務的完全限定類名,應在用戶查看聯系人時接收通知。此通知由設備的聯系人應用程序發送;它允許您的應用程序推遲數據密集型操作,直到需要它們為止。例如,您的應用程序可以通過讀入并顯示聯系人的高分辨率照片和最新的社交流項目來響應此通知。社交流交互部分中更詳細地描述了此功能。您可以在SampleSyncAdapter示例應用程序的NotifierService.java文件中看到通知服務的示例。
viewGroupActivity
應用程序中可顯示組信息的活動的完全限定類名。當用戶單擊設備的聯系人應用程序中的組標簽時,將顯示此活動的UI。
viewGroupActionLabel
聯系人應用程序為UI控件顯示的標簽,允許用戶查看應用程序中的組。
例如,如果您在自己的設備上安裝Google+應用程序并將Google+與聯系人應用程序同步,則會在聯系人應用程序的“組”標簽中看到Google+圈子列為群組。如果您點擊Google+圈子,您會看到該圈子中的人員被列為“群組”。在屏幕頂部,您會看到Google+圖標;如果您點擊它,控制權切換到Google+應用。聯系人應用程序使用Google+圖標作為viewGroupActionLabel的值,使用viewGroupActivity執行此操作。
此屬性允許使用字符串資源標識符。
viewStreamItemActivity
應用程序中活動的完全限定類名,當用戶單擊原始聯系人的流項時,設備的聯系人應用程序將啟動該活動。
viewStreamItemPhotoActivity
應用程序中活動的完全限定類名,當用戶單擊原始聯系人的流項目中的照片時,設備的聯系人應用程序將啟動該活動。
<ContactsDataKind>元素
<ContactsDataKind>元素控制聯系人應用程序UI中應用程序的自定義數據行的顯示。它具有以下語法:
<ContactsDataKindandroid: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。
其他聯系人提供商功能
除了前面部分中描述的主要功能外,Contacts Provider還提供了這些用于處理聯系人數據的有用功能:
- 聯系小組
- 照片功能
聯系小組
聯系人提供者可以選擇使用組數據標記相關聯系人的集合。如果與用戶帳戶關聯的服務器要維護組,則帳戶的帳戶類型的同步適配器應在聯系人提供程序和服務器之間傳輸組數據。當用戶將新聯系人添加到服務器,然后將此聯系人放入新組時,同步適配器必須將新組添加到ContactsContract.Groups表。原始聯系人所屬的組或組使用ContactsContract.CommonDataKinds.GroupMembership MIME類型存儲在ContactsContract.Data表中。
如果您正在設計將原始聯系人數據從服務器添加到聯系人提供程序的同步適配器,并且您沒有使用組,則需要告知提供程序使您的數據可見。在用戶向設備添加帳戶時執行的代碼中,更新Contacts Provider為該帳戶添加的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.Photo和ContactsContract.RawContacts.DisplayPhoto的參考文檔包含檢索照片信息的示例。檢索原始聯系人的主縮略圖沒有便利類,但您可以向ContactsContract.Data表發送查詢,選擇原始聯系人的_ID,Photo.CONTENT_ITEM_TYPE和IS_PRIMARY列以查找原始聯系人的主要聯系人照片排。
人的社交流數據還可以包括照片。它們存儲在android.provider.ContactsContract.StreamItemPhotos表中,在社交流照片一節中有更詳細的描述。
總結
以上是生活随笔為你收集整理的Android官方文档—APP组件(Content Providers)(Contacts Provider)的全部內容,希望文章能夠幫你解決所遇到的問題。