作者简介 原创微信公众号郭霖 WeChat ID: guolin_blog
本篇是李政第三篇投稿,分享了实现监听通讯录联系人变化的过程,希望能够帮助到大家。
李政的博客地址:
http://blog.csdn.net/lz8362
前言
大家在使用微信的过程中,可以发现微信的通讯录可以和手机通讯录保持一致的增删变化,这点特性属于应用的易用性范畴,今天就来讲讲如何实现该功能。
通讯录涉及到的表统一集中在contacts2.db中。其路径如下:
/data/data/com.android.providers.contacts/databases/contacts2.db
统一由 ContactsContract类 进行管理,我们经常调用的 Phone、Contacts、RawContacts 都是 ContactsContract 的子类,常见的读取操作一共涉及到三张表,数据路径为:
三者关系如下:
contacts表 中每一行是一个联系人,每个联系人对应着一个唯一的_id,每个联系人对应着raw_contacts表中一行或多行数据,通过 contact_id 确定对应关系。
raw_contacts表 张每一行是某个联系人的联系信息,当有联系信息发生改变时,会修改其 version 字段。raw_contacts表 会通过把_id写入到 data表 的 raw_contact_id 字段中的方式,对应指定的详细信息,raw_contacts表 的每一行对应一行或多行data表中数据。
data表 通过 raw_contact_id 确定其属于 raw_contacts表 哪一个联系人。每一行通过一个 mimetype_id 的字段来表示该行存储的是什么类型的数据,该字段引用了 mimetyps表,此表存储了常用的数据类型。
通过比较,Row_contacts表 具有承上启下的作用,ContentObserver 是Android用来提供共享数据变化后操作的监听类,其抽象方法 onChange() 会在通讯录数据库发生变化后被回调。由 ContentResolver(Android用来对程序的共享数据进行增删改查)完成注册和管理。
所以,通讯录的实时监听,可以通过 ContactObserver 对 raw_contacts表 进行监听来实现。
监听通讯录需要声明的权限只需读取/写入联系人权限即可,如下:
android.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
实施步骤
一、在应用开始时,先对通讯录信息进行一次备份,这里用 ContentResoler 对 RawContacts.CONTENT_URI 进行查询时,只需读取 RawContacts._ID 和 RawContacts.VERSION 两个字段即可。将查询结果保存在Map中(暂且命名为baseMap)。查询语句如下:
RawContacts.VERSION 标志着本行数据的更新状态,每当有本行数据有更新时,该标记会加1,可以用它来进行联系人变更的判断。
重点说下 sort_key,sort_key 在这里是用来排序的,该字段本身是用来存放联系人首字母的,不管是在Android4.4之前还是之后,这个字段始终都是有值存在的,只是从首字母到联系人全拼音的区别,有的机型会是拼音加汉字的组合。
可能会有同学说Android4.4之后的首字母是保存的 phonebook_label 这个字段上,很遗憾,当你真正去适配各种手机型号时,你会发现 phonebook_label 会有为空的情况,这种现象源于如今流行的ROM定制,你不能保证厂商在定制过程中会墨守成规,这种时候就看你的变通了。经过测试发现,对sort_key进行截取操作,获取其第一位字符,其效果和直接获取首字母有异曲同工之妙。
二、开启监听服务,注册 ContentObserver,在其 onchange() 方法中执行查询操作。
在 onCreate() 方法中执行注册操作,操作如下:
其中 observer 即为 ContentObserver 的实现对象。
RawContacts.CONTENT_URI 为本监听器监听的数据表,即为 raw_contacts表。由于 raw_contacts表 记录的数据是联系人的联系信息,包含通话记录、联系信息等,所以当用拨打电话、接听电话、增删修改联系人时,都会去更新 raw_contacts表。虽然理论上会导致对 RawContacts.CONTENT_URI 的监听被重复触发,但是不代表这种情况会一直发生。
实际上,在使用应用的过程中,出现接听电话、拨打电话的概率是可以忽略的,而用户通讯录的联系人条目也不会出现万级这种情况,对于 raw_contacts表 偶尔出现的多次查询不会对应用本身产生影响。本身通讯录同步更新是一个易用性的体验,考虑太多,只会无从下手。
三、当 onchange() 被触发时,首先保存一个通讯录变更标记 change 为 true,存储在 SharedPreferences 中,再执行查询 raw_contacts表 操作,操作同第一步。但是需要额外对 Cursor 得到的行数进行判断:
后续执行查询操作,查询数据保存到Map中(暂且即为realizeMap)
比较 baseMap 和 realizeMap 之间的区别,通过比较 RawContacts._ID,分为以下三种情况:
(1)、baseMap中存在,realizeMap中不存在的,即为删除。
(2)、baseMap中不存在,realizeMap中存在的,即为新增。
(3)、baseMap中存在,realizeMap中也存在的,但是 RawContacts.VERSION 变了,即为修改。
这里之所以要加上行数判断,因为在实际测试过程中,我发现,当手机在通话过程后,部分手机型号此时会禁止查询 raw_contacts表,从而导致监听服务获取不到数据,当应用重新回到前台时,才能正常查询。所以通过设置一个更新标记,当应用重新回到前台时,可以及时的通过标记再次执行更新操作。
四、选取需要更新的 RawContacts._ID 获取其详细内容,查询 data表(不用考虑删除的,其ID已不存在),代码如下:
Data表中记录着联系人的详细信息,使用选择语句
Data.MIMETYPE + “=’” + Phone.CONTENT_ITEM_TYPE + “’”
可以提出无关联系人选项,只保留手机联系人选项。Data.RAW_CONTENT_ID 与RawContacts._ID 是多对一的,连个选择语句配合使用才能确定最终需要的数据。
监听通讯录变化整个流程就介绍完了。大家可以尝试动手试一下。
文章原创作者GuoLin 书籍推荐
郭林大神原创android 书籍:《第一行代码 android》
网友评论