ContactsProvider2
本篇不全也不細(xì),只是根據(jù)按照個(gè)人理解和工作中遇到的問(wèn)題,總結(jié)了個(gè)人認(rèn)為的要點(diǎn)。
1. Android的數(shù)據(jù)庫(kù)體系
- 1.1. 概述
- 1.2 uri結(jié)構(gòu)
2. ContactsProvider2
- 2.1. 概述
- 2.2. Contacts2.db中的表
- 2.3. ContactProvider2中的實(shí)現(xiàn)
- 2.4. 批量訪問(wèn)
1. Android的數(shù)據(jù)庫(kù)體系
1.1. 概述
Android的數(shù)據(jù)庫(kù)體系可以分為三個(gè)層次:
ContentProvider不支持線程安全,但SQLite是支持線程安全的(編譯前將 宏SQLITE_THREADSAFE 設(shè)置為1)。
多個(gè)進(jìn)程可以同時(shí)打開一個(gè)database,而且可以同時(shí)執(zhí)行SELECT操作,但是同一時(shí)間只能有一個(gè)進(jìn)行修改操作。
下面的簡(jiǎn)圖展示了我們使用uri訪問(wèn)database的方式,其中ContentProvider在ActivityManagerService中的register可能是自己所在進(jìn)程啟動(dòng)時(shí)做的,也可能是ActivityManagerService收到uri請(qǐng)求后啟動(dòng)ContentProvider時(shí)做的。ContentResolver和ActivityManagerService根據(jù)uri中的"authority"來(lái)確定要訪問(wèn)的ContentProvider。
1.2 uri結(jié)構(gòu)
基本結(jié)構(gòu)
[scheme:]scheme-specific-part[#fragment]
進(jìn)一步劃分
[scheme:][//authority][path][?query][#fragment]
path可以有多個(gè),每個(gè)用/連接,比如scheme://authority/path1/path2/path3?query#fragment。
query參數(shù)可以帶有對(duì)應(yīng)的值,也可以不帶,如果帶對(duì)應(yīng)的值用=表示,如:
scheme://authority/path1/path2/path3?id = 1#fragment,這里有一個(gè)參數(shù)id,它的值是1
query參數(shù)可以有多個(gè),每個(gè)用&連接,如
scheme://authority/path1/path2/path3?id = 1&name = xiaofang&age#fragment
這里有三個(gè)參數(shù):
參數(shù)1:id,其值是:1
參數(shù)2:name,其值是:xiaofang
參數(shù)3:age,沒有對(duì)它賦值
在android中,scheme、authority都是必須要有的,而至于path、query和fragment,它們都是選擇性的,可以有也可以沒有,但順序不能變,比如:
"path"可不要:scheme://authority?query#fragment
"path"和"query"可都不要:scheme://authority#fragment
"query"和"fragment"可都不要:scheme://authority/path
“path”,“query”,"fragment"都不要:scheme://authority
等等……
終極劃分
[scheme:][//host:port][path][?query][#fragment]
2. ContactsProvider2
2.1. 概述
ContactsProvider2是Android一個(gè)很成熟的源生組件,用于管理聯(lián)系人數(shù)據(jù)相關(guān)的存儲(chǔ)區(qū)。Google對(duì)ContactsProvider2有一個(gè)比較詳細(xì)的介紹,下面是相關(guān)鏈接:
https://developer.android.com/guide/topics/providers/contacts-provider
ContactsProvider2管理了兩個(gè)database,一個(gè)是"contacts2.db",另外一個(gè)是"profile.db";本篇著重寫contacts.db相關(guān)的內(nèi)容。
2.2. Contacts2.db中的表
“contacts2.db”數(shù)據(jù)庫(kù)中存儲(chǔ)了聯(lián)系人、通話記錄和Account等數(shù)據(jù)。
由于聯(lián)系人的信息字段比較多,包含了號(hào)碼、地址、Email和頭像等信息,所以數(shù)據(jù)庫(kù)中的數(shù)據(jù)表也比較多;為了提高查詢速度,還創(chuàng)建了視圖和索引;另外還有觸發(fā)器。
Android P源碼中數(shù)據(jù)表有30多個(gè),視圖有11個(gè);這里就不一一列出了,太占地方了,哈哈。
我們需要搞清楚這些表之間的關(guān)系,特別是幾個(gè)存儲(chǔ)重要信息的表,下面是幾個(gè)重要表的ER圖
表“Contacts”中的數(shù)據(jù)是在數(shù)據(jù)插入表“raw_contact”之后,在Transaction的回調(diào)onCommit調(diào)用序列中插入的。
聯(lián)系人相關(guān)的數(shù)據(jù)并沒有存儲(chǔ)在raw_contact表中,而是存儲(chǔ)在了Data表中;一條raw_contact數(shù)據(jù)在Data表中對(duì)應(yīng)多條數(shù)據(jù)。Data表中有15個(gè)data字段,每個(gè)字段存儲(chǔ)的是什么信息要根據(jù)列"mimetype_id"來(lái)決定,該列的取值來(lái)自"account"表的主鍵"_ID"。
協(xié)定類ContactsContract.java中為data字段定義了映射名稱類,如下表:
| ContactsContract.CommonDataKinds.StructuredName | 與該數(shù)據(jù)行關(guān)聯(lián)的raw contact的姓名數(shù)據(jù)。 | 一位raw contact只有其中一行。 |
| ContactsContract.CommonDataKinds.Photo | 與該數(shù)據(jù)行關(guān)聯(lián)的raw contact的主要照片。 | 一位raw contact只有其中一行。 |
| ContactsContract.CommonDataKinds.Email | 與該數(shù)據(jù)行關(guān)聯(lián)的raw contact的電子郵件地址。 | 一位raw contact可有多個(gè)電子郵件地址。 |
| ContactsContract.CommonDataKinds.StructuredPostal | 與該數(shù)據(jù)行關(guān)聯(lián)的raw contact的郵政地址。 | 一位raw contact可有多個(gè)郵政地址。 |
| ContactsContract.CommonDataKinds.GroupMembership | 將raw contact鏈接到聯(lián)系人提供程序內(nèi)某個(gè)組的標(biāo)識(shí)符。 | 組是帳戶類型和帳戶名稱的一項(xiàng)可選功能。聯(lián)系人組部分對(duì)其進(jìn)行了更詳細(xì)的描述。 |
以“ContactsContract.CommonDataKinds.Email”和“ContactsContract.CommonDataKinds.StructuredName”為例,前者用于聯(lián)系人的Email信息,后者用于聯(lián)系人的名字信息; 它們分別在Data表中有一條記錄,而且都使用了“data1”、“data2”和“data3”這三個(gè)字段,但是每個(gè)字段在兩條記錄中分別存儲(chǔ)了不同的信息,如下圖:
2.3. ContactProvider2中的實(shí)現(xiàn)
下面是ContactsProvider2的類繼承關(guān)系圖:
ContactsProvider2直接繼承了AbstractContactsProvider.java這個(gè)類,它的很多功能都繼承自這個(gè)抽象父類,看代碼時(shí)這個(gè)抽象父類的代碼也是重點(diǎn)。
ContactsProvider2中定義了UriMatcher對(duì)象sUriMatcher,并在static塊中進(jìn)行了初始化:
ContractsProvider2會(huì)將接收到的uri和sUriMatcher進(jìn)行匹配,即根據(jù)uri中的path確定要訪問(wèn)的table。
比如,我們向Data表中增加一條數(shù)據(jù)記錄,那么我們會(huì)使用
insertInTransaction方法使用sUriMatcher進(jìn)行匹配后返回”DATA”,然后在switch的對(duì)應(yīng)case語(yǔ)句中處理這條增加數(shù)據(jù)的請(qǐng)求。
由于"contacts2.db"中的表比較多,而App層只是通過(guò)uri發(fā)送請(qǐng)求,那么相關(guān)表直接的數(shù)據(jù)維護(hù)邏輯是在ContactsProvider2中完成的,這也方便了App層的使用。為了維護(hù)數(shù)據(jù)一致性和原子性等,ContactsProvider2.java的增/刪/改都使用了事務(wù)(Transaction),下面的流程圖展示了insert操作的事務(wù)處理流程,其他操作類似:
2.4. 批量訪問(wèn)
可以使用 ContentProviderOperation 類中的方法創(chuàng)建一批訪問(wèn)調(diào)用,然后通過(guò) ContentResolver.applyBatch(…) 應(yīng)用這些調(diào)用。如果某次批量修改需執(zhí)行大量操作,則可能會(huì)阻塞其他進(jìn)程,導(dǎo)致整體用戶體驗(yàn)不佳。可以將想執(zhí)行的修改操作盡量分散到各個(gè)集合中,并為一項(xiàng)或多項(xiàng)操作設(shè)置掛起點(diǎn)。掛起點(diǎn)是一個(gè) ContentProviderOperation 對(duì)象,并且其 isYieldAllowed() 值設(shè)置為 true。當(dāng)ContactsProvider2遇到掛起點(diǎn)時(shí),它會(huì)暫停其工作,讓其他進(jìn)程運(yùn)行,并關(guān)閉當(dāng)前事務(wù)。當(dāng)ContactsProvider2再次啟動(dòng)時(shí),它會(huì)繼續(xù)執(zhí)行 ArrayList 中的下一項(xiàng)操作,并啟動(dòng)新事務(wù)。
掛起點(diǎn)會(huì)導(dǎo)致每次調(diào)用 applyBatch() 時(shí)產(chǎn)生多個(gè)事務(wù)。因此,應(yīng)為一組相關(guān)數(shù)據(jù)行的最后一項(xiàng)操作設(shè)置掛起點(diǎn)。例如,應(yīng)該為一組操作中添加raw contact行及其關(guān)聯(lián)數(shù)據(jù)行的最后一項(xiàng)操作設(shè)置掛起點(diǎn),或者針對(duì)一組與某位聯(lián)系人相關(guān)的行的最后一項(xiàng)操作設(shè)置掛起點(diǎn)。
掛起點(diǎn)也是一個(gè)原子操作單元。無(wú)論訪問(wèn)成功還是失敗,兩個(gè)掛起點(diǎn)之間的所有訪問(wèn)均以一個(gè)單元的形式呈現(xiàn)。如果不設(shè)置任何掛起點(diǎn),則最小的原子操作即為整個(gè)批量操作。如果使用掛起點(diǎn),則可以防止操作降低系統(tǒng)性能,還可確保部分操作為原子操作。
結(jié)束
總結(jié)
以上是生活随笔為你收集整理的ContactsProvider2的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: protues 选项卡说明
- 下一篇: Linux php5 curl 扩展