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