天天看點

Android通訊錄資料庫操作

1. contactsprovider2.java (實作了contentprovider)

2. contactscontract.java 所有的聯系人uri,與聯系人相關資料庫字段均在此定義。這裡面包含以下要講的contacts表,raw_contacts表,data表,phone_lookup表的字段及uri描述。

table contacts

_id

starred

lookup

photo_id

1

 省略

<null>

2

3

2366

starred:辨別該賬戶是否為vip賬戶

lookup: 沒具體看,也是一個非常重要的字段

photo_id: 引用data表的 _id,

table raw_contacts

contact_id

account_name

account_type

sync1

[email protected]

com.google

http://www.google.com/m8/feeds/contacts/[email protected]/base2_property-android/1ac2a39e89c441cc

pcsc

com.htc.android.pcsc

contact_id:引用contacts表的_id字段

account_name:指明該聯系人是從哪個賬戶上同步下載下傳下來的。

account_type:郵件帳戶的類型

sync1:說明該聯系人資訊是與哪個網址進行互動(同步聯系人資訊)

table data.

raw_contact_id

mimetype_id

data1

data2

data15

6

南京用友

5

1-318-296-4252

司道鵬

4

1-516-920-8116

子江

1795113734017562

7

+8613734017562

‰png

data15: 存儲照片 (type: blob)

table phone_lookup

data_id

normalized_number

25246928131

61180296151

2657104373115971

2657104373168+

data_id: 引用 data 表的_id字段

raw_contact_id: 引用raw_contacts表的 _id字段

normalized_number:聯系人電話号碼的倒序排列

如何将smsmms.db資料庫與contacts2.db資料結合起來成為了非常關鍵的部分

依據聯系人的number,查詢該聯系人的contact_id

phone.content_uri, = “content:// com.android.contacts/data/phones”

通過檢視源代碼發現:該uri主要對應着contacts表,raw_contacts表,data表。這段源碼對于剛了解該contact2資料庫的人說比較費勁,

“qb.setprojectionmap(distinct ? sdistinctdataprojectionmap : sdataprojectionmap);”是非常重要的線索,它告訴我們會查詢哪些字段

在這裡尤其需要說明的是:phonenumberutils.compare(string a, string b) 方法

加入我們在手機中把“13466739143”存儲為聯系人“張三”(存儲的聯系手機号僅僅是13466739143),如果張三使用飛信給你發短信(我們知道用飛信,電話号碼前會自動加12593),使用者肯定希望在手機上的未接短信來自張三,而不是有“1259313466739143”被當作一個陌生号碼出現

如何将“1259313466739143”和“13466739143”以及可能的“1795113466739143”,“+8613466739143”等等認為是同一個電話号碼,這裡的phonenumberutils.compare(string a, string b)就起到了非常關鍵的作用。

有了contact_id之後,我們就可以做很多事情。

1.依據contact_id,去查詢該聯系人的照片:

2. 依據該聯系人的contact_id, 去查詢該聯系人的名字(比如“張三”)

3. 依據該聯系人的contact_id ,查詢同一個contact_id有多少個電話号碼。

  (有可能手機上存了張三2個号碼)

------------------------------------------------------------------------------

如果需要讀取一個聯系人的資訊用content_lookup_rui代替content_uri

如果需要通過電話号碼查找一個聯系人,用phonelookup.content_fiilter_uri,這個uri為這個目的進行了優化;

如果需要通過部分名字的比對查找,用content_filter_uri;

android中的通信錄操作給人的第一感覺就是暈,不知道怎麼去用。可以看下這張我畫的圖:(比較醜,但是地球人都能看懂)

Android通訊錄資料庫操作

這幅圖的意思是: 操作通信錄本質是去操作合适的contentprovider,通過合适的contentprovider去操作通訊錄 。

1: 要想熟練的操作合适的contentprovider,就必須要掌握一個類contactscontract(2.0開始使用)。

現在可以思考一個新問題: android中的聯系人資訊是如何存儲的?

android 中的聯系人資訊都是存儲在一個叫contacts2.db的資料庫中。資料庫的路徑是:/data/data /com.android.provider.contacts/databases/contacts2.db(在這裡推薦一個sqlite的檢視工 具:

http://www.sqliteexpert.com/  個人版是免費的)

繼續思考一個新問題: 這個資料庫是如何來存儲聯系人資訊的?

根據官方的文檔:通信錄是一個3層的資料存儲模型(初看挺牛的,說穿了就是3張表)

我又畫了一張圖比較形象的反應這個“3層模型”。

Android通訊錄資料庫操作

第一層:data層,每種獨立的資料類型占一行。具體哪些獨立的資料可以占一行,可以在mimetypes這張表中找到, 原生android的系統 一共12種,例如name,phone,email ect..

第二層:rawcontracts層,由data層的多條資料組合成一個完整的聯系人資訊。

第 三層:contracts層,這一層主要注意與第二層的差別。大部分情況下這兩層的資料時指同一個聯系人的資訊,即他們倆是一一對應的關系,但是有些特殊 情況,這個我是查了一些老外的論壇加上自己的了解,例如 我做一個本地通信錄和網絡上的通信錄同步的時候,可能有一個人他在本地存在,他在網絡上也存在,這個時候android就可以識别他們,認為他們兩個其實 是指同一個人。 (這種情況我沒有試出來,我感覺這個其實是android創造了這個概念之後,留給我們開發自己去實作的。)

上面說過“要想熟練的操作合适的contentprovider,就必須要掌握一個類contactscontract”,

那麼這個contactscontract類是幹什麼的呢 ?

說穿了這個類就是去解釋和翻譯這個contacts2.db資料庫的。 

這個類超大6000+行代碼, 但是确沒有什麼操作代碼,幾乎都是來解釋contacts2.db資料庫的。

這個類中有很多的内部接口和内部類,用來翻譯一些表。 例如data内部類,rawcontacts内部類,等等。

下面我以一個實際的例子講解一下操作過程:

問題: 我有一個電話号碼,現在想去查找這個電話号碼主人的姓名。

第一步:我要确定用哪一個contentprovider去查詢,這個時候肯定想到去用含有電話号碼的contentprovider去查詢電話号碼所在的contacts_id.

第二部:通過contacts_id找到對應的raw_contacts_id.

第三部:通過raw_contacts_id找到對應的聯系人姓名.

确定了操作步驟之後,肯定是去看文檔了,誰也不能猜出來含有電話号碼contentprovider_uri.

通過查詢文檔: if you need to look up a contact by the phone number, usephonelookup.content_filter_uri, which is optimized for this purpose.

我們确定了uri之後就可以開始編寫代碼了。

看完這段代碼,你可能想去問,第二步跟第三步跑哪裡去了。這兩步其實已經被封裝在resoler的query方法中了。如果真要我們編寫這兩部的話,那我們還不如不用contentprovider來的輕松。

最後再來說下真機上的通信錄模型,真機上的通信錄其實就是原生通信錄的擴充。增加一些東西。

最重要的一點,原生資料庫裡有的表,表字段,觸發器,視圖或索引,在真機上肯定也有。

如果想自己做一個通信錄,也肯定要在原生的通信錄上擴充,隻能增加,不能減少。

想問為什麼?

如果你少兩張表的話,可能一些系統功能就崩潰了。

這就不符合google的目的了,我提供了一些功能,你可以使用修改,但是你别删除,是以google火了。

添加一條資料

---------------------------------------------------------

android系統中的聯系人也是通過contentprovider來對外提供資料的,我們這裡實作擷取所有聯系人、通過電話号碼擷取聯系人、添加聯系人、使用事務添加聯系人。

擷取所有聯系人

1.   android系統中的聯系人也是通過contentprovider來對外提供資料的

2.   資料庫路徑為:/data/data/com.android.providers.contacts/database/contacts2.db

3.   我們需要關注的有3張表

     raw_contacts:其中儲存了聯系人id

     data:和raw_contacts是多對一的關系,儲存了聯系人的各項資料

     mimetypes:為資料類型

4.   provider的authorites為com.android.contacts

5.   查詢raw_contacts表的路徑為:contacts

6.   查詢data表的路徑為:contacts/#/data

     這個路徑為連接配接查詢,要查詢“mimetype”字段可以根據“mimetype_id”查詢到mimetypes表中的資料

7.   先查詢raw_contacts得到每個聯系人的id,在使用id從data表中查詢對應資料,根據mimetype分類資料

示例:

通過電話号碼擷取聯系人

1.   系統内部提供了根據電話号碼擷取data表資料的功能,路徑為:data/phones/filter/*

2.   用電話号碼替換“*”部分就可以查到所需資料,擷取“display_name”可以擷取到聯系人顯示名

添加聯系人

1.   先向raw_contacts表插入id,路徑為:raw_contacts

2.   得到id之後再向data表插入資料,路徑為:data

使用事務添加聯系人

1.   在添加聯系人得時候是分多次通路provider,如果在過程中出現異常,會出現資料不完整的情況,這些操作應該放在一次事務中

2.   使用contentresolver的applybatch(string authority,arraylist<contentprovideroperation> operations) 方法可以将多個操作在一個事務中執行

3.   文檔位置:

      file:///f:/android-sdk-windows/docs/reference/android/provider/contactscontract.rawcontacts.html