学徒浅析Android开发——通讯录实时监听的设计实现

来源:互联网 发布:复式记账软件 编辑:程序博客网 时间:2024/05/17 18:19

 本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布


大家在使用微信的过程中,可以发现微信的通讯录可以和手机通讯录保持一致的增删变化,这点特性属于应用的易用性范畴,今天就来讲讲如何实现该功能。

 

    通讯录涉及到的表统一集中在contacts2.db中。其路径如下:

 /data/data/com.android.providers.contacts/databases/contacts2.db

统一由ContactsContract类进行管理,我们经常调用的Phone、Contacts、RawContacts都是ContactsContract的子类,常见的读取操作一共涉及到三张表,数据路径为:

Content://com.android.contacts/contacts         联系人表           Contacts类

Content://com.android.contacts/raw_contacts     联系人联系信息表   RawContacts类

Content://com.android.contacts/data            联系人详细信息表   Data类

三者关系如下:


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_CONTACTSandroid.permission.WRITE_CONTACTS

实施步骤:

一、       在应用开始时,先对通讯录信息进行一次备份,这里用ContentResoler对RawContacts.CONTENT_URI进行查询时,只需读取RawContacts._ID和RawContacts.VERSION两个字段即可。将查询结果保存在Map中(暂且命名为baseMap)。查询语句如下:

String[] args = new String[]{    “sort_key”,    RawContacts._ID,RawContacts.VERSION};Cursor cursor =getContentResolver().query(RawContacts. CONTENT_URI,args,RawContacts.DELETED+””””==0 and 1==”+RawContacts.DIRTY,null,args[0] + “ COLLATE LOCALIZED asc” );

   RawContacts.VERSION 标志着本行数据的更新状态,每当有本行数据有更新时,该标记会加1,可以用它来进行联系人变更的判断。

重点说下sort_key,sort_key在这里是用来排序的,该字段本身是用来存放联系人首字母的,不管是在Android4.4之前还是之后,这个字段始终都是有值存在的,只是从首字母到联系人全拼音的区别,有的机型会是拼音加汉字的组合。可能会有同学说Android4.4之后的首字母是保存的phonebook_label这个字段上,很遗憾,当你真正去适配各种手机型号时,你会发现phonebook_label会有为空的情况,这种现象源于如今流行的ROM定制,你不能保证厂商在定制过程中会墨守成规,这种时候就看你的变通了。经过测试发现,对sort_key进行截取操作,获取其第一位字符,其效果和直接获取首字母有异曲同工之妙。

二、       开启监听服务,注册ContentObserver,在其onchange()方法中执行查询操作

在onCreate()方法中执行注册操作,操作如下:

getContentResolver.registerContentObserver(RawContacts.CONTENT_URI,true,observer);

     其中observer即为ContentObserver的实现对象。

RawContacts.CONTENT_URI为本监听器监听的数据表,即为raw_contacts表。由于raw_contacts表记录的数据是联系人的联系信息,包含通话记录、联系信息等,所以当用拨打电话、接听电话、增删修改联系人时,都会去更新raw_contacts表。虽然理论上会导致对 RawContacts.CONTENT_URI的监听被重复触发,但是不代表这种情况会一直发生。实际上,在使用应用的过程中,出现接听电话、拨打电话的概率是可以忽略的,而用户通讯录的联系人条目也不会出现万级这种情况,对于raw_contacts表偶尔出现的多次查询不会对应用本身产生影响。本身通讯录同步更新是一个易用性的体验,考虑太多,只会无从下手。

三、       当onchange()被触发时,首先保存一个通讯录变更标记change为true,存储在SharedPreferences中,再执行查询raw_contacts表操作,操作同第一步。但是需要额外对Cursor得到的行数进行判断:

 if( 0 ==cursor.getCoutn()){   return;}else{   变更标记change为false;}

后续执行查询操作,查询数据保存到Map中(暂且即为realizeMap)

比较baseMap和realizeMap之间的区别,通过比较RawContacts._ID,分为以下三种情况:
(1)、baseMap中存在,realizeMap中不存在的,即为删除。

(2)、baseMap中不存在,realizeMap中存在的,即为新增。

(3)、baseMap中存在,realizeMap中也存在的,但是RawContacts.VERSION变了,即为修改。

这里之所以要加上行数判断,因为在实际测试过程中,我发现,当手机在通话过程后,部分手机型号此时会禁止查询raw_contacts表,从而导致监听服务获取不到数据,当应用重新回到前台时,才能正常查询。所以通过设置一个更新标记,当应用重新回到前台时,可以及时的通过标记再次执行更新操作。

四、       选取需要更新的RawContacts._ID获取其详细内容,查询data表(不用考虑删除的,其ID已不存在),代码如下:

String[] args = new String[]{  Phone._ID,  Phone.NUMBER,  Phone.DISPLAY_NAME,s  PHONE.CONTACT_ID,  “sort_key”};String id = 待更新的IDCursor cursor =getContentResolver().query(  Data.CONTENT_URI,  args,  Data.RAW_CONTENT_ID + “=? and ” + Data.MIMETYPE +“=’” + Phone.CONTENT_ITEM_TYPE + “’”,new String[]{ id },null);

Data表中记录着联系人的详细信息,使用选择语句

Data.MIMETYPE + “=’” + Phone.CONTENT_ITEM_TYPE + “’”

可以提出无关联系人选项,只保留手机联系人选项。Data.RAW_CONTENT_ID与RawContacts._ID是多对一的,连个选择语句配合使用才能确定最终需要的数据。

 

监听通讯录变化整个流程就介绍完了。大家可以尝试动手试一下。


1 0