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
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中的通信錄操作給人的第一感覺就是暈,不知道怎麼去用。可以看下這張我畫的圖:(比較醜,但是地球人都能看懂)
這幅圖的意思是: 操作通信錄本質是去操作合适的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層模型”。
第一層: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