有没有办法获取最近操作的联系人

转载 2015年03月11日 20:02:53


最近想做一个功能,能够获取android最近修改的联系人,包括added/updated/deleted。虽然最后还是没有找到很好的办法,但顺便学习了下Android contacts content provider机制。

Android contacts content provider是一个系统级别的content provider。content provider提供了一种机制,使得一个应用可以开放出接口供其他应用跨进程调用。而contact content provider则是系统提供的。其调用者(content resolver)可以通过该provider获取通讯簿数据。

在Android系统中,通讯簿是通过三层表结构来存储数据的,从上到下分别是Contact,RawContact和Data。

先不管Android自己的实现。如果让我们设计数据库来保存通讯簿,会怎么做呢?一种做法是只有一个表,其中包括ID,Name,Phone,Email,Address等项。其问题在于一个用户可以有多个电话,多个邮件地址。关系型数据库处理这种一对多的关系的方式就是分成两个表,第一个表里只有ID和Name;第二个表里每行对应着一个联系方式,例如第一行是Phone1,第二行Phone2,第三行Email1,...。对于每一行,需要有一个指向第一表的索引。所以第二张表一般包含下面几列:ID,ContactID(作为外键),type(类型,例如是邮件还是电话),data(具体的电话号码,邮件地址等)。

在Android contact content provider中,RawContact就是第一张表,Data就是第二张表。那为何还有Contact表呢?

我们先做个实验。现在有一个干净的系统,没有设置帐户,也没有添加过联系人;另外有一个干净的gmail帐号,example1@gmail.com。此时我们点击拨号界面里的新加联系人,系统会提示“本地保存”还是“添加帐户”,这时候如果选择本地保存,那该联系人只是保存的机器本地的。假设联系人为张三。然后我们添加example1@gmail.com为系统帐户。现在系统会进行同步。如果通过web登录gmail,会发现张三已经在gmail的联系人中了。如果在web的gmail中添加李四,那李四也会被同步的手机上。如果从系统移除了example1@gmail.com,那张三和李四就都会从通讯簿中删除。如果在加上example1@gmail.com,那张三和李四就又回来了。

这个实验说明如果有系统帐号,则本地的联系人会和服务端的联系人保持一致,互相更新。一旦有了系统帐号,所有的联系人都会有一个源(source)。对于上面的例子,张三和李四的源就是example1@gmail.com。在没有系统帐号是添加的用户(张三),其开始时没有源的,但一旦添加了系统帐号,张三的源就设置为第一个添加的系统帐号。当系统帐号被移除时,表示contacts的源被移除,所以以该帐号为源的contacts都会被移除。

Android系统可以设置多个系统帐号,所以contacts可以支持多个源。假设现在已经添加了example1@gmail.com,现在我又添加了example2@gmail.com。现在系统中就有两个sources。此时再添加contact,则可以选择是添加到example1@gmail.com还是example2@gmail.com中。每个源对应的contacts独立跟服务器进行同步。

现在问题来了,如果我在example2@gmail.com中也添加了张三。而此张三的电话跟example1@中的电话不同。一种处理方式是被当做完全不同的两个contact;另外一种方式是合并二者。Android采用了第二种方式。所以如果我此时在web上对example2@添加了张三,那同步之后,张三下面会有两个电话号码,分别是example1@和example2@下面添加的。这种归并从现象上看是通过联系人名称来进行的,所以如果重名用户,可能会发生不错误的合并。另外,一个比较诡异的现象是如果对example2@是在手机上添加张三,那联系人中会有两个张三;但之后如果删除两个账户,再添加回来,则会进行归并。估计此归并操作是在sync的时候做的。

归并后的联系人就是Contact表,为支持这种1对多关系,RawContact表中每项又有一个指向Contact表的ID的外键。

这三张表都存放在contacts2.db中,只有系统才能访问。为了让普通进程也能操作,系统提供了contact content provider开发访问接口,三者对应的URI分别是ContactsContract.Contact.CONTENT_URI,ContactsContract.RawContact.CONTENT_URI和ContactsContract.Data.CONTENT_URI。然后我们就可以通过content resolver访问和操作这些数据了。contact contect provider通过两个权限READ_CONTACTS和WRITE_CONTACTS来限制访问,需要的应用可以在manifest中设置。

具体的使用可以参考http://developer.android.com/guide/topics/providers/contacts-provider.html。本文前面也很多是参考它写的。

回到开始的问题,有没有办法获取最近操作的联系人呢?

我们可以注册ContentObserver来监听这Contact表对应的URI的变化。如果手动添加,删除或者修改contact,会触发;如果在web上修改然后同步下来,也会触发;如果删除或添加系统帐户同步下来,同样会触发。这都是我们需要的。同时,如果拨打了某个电话,则同样会触发,因为饿Contact有相关的列。还有其他可能导致触发的情况。

即使这些变化都是我们需要的,那此时我们只能query到所有的contact。

从API18开始,在Contact表中,新加了CONTACT_LAST_UPDATED_TIMESTAMP列,表示该contact被更新的时间。所以我们可以对其进行排序和过滤。我们可以记录每次变化触发的时间,那么当前时间和上次触发时间之间的contacts,就是最近变化的。通过这种方式,可以知道最近inserted/updated的contact。

对于deleted contact,也是从API18开始,新加了DeletedContacts表,其中记录了被删除的contact的日志,有两列:ID和timestamp。我们仍然可以对timestamp进行排序或过滤,找到此次和上次变化之间的contacts。但问题是这里只能拿到ID,而ID此时已经无法查到对应的contact了(因为已经删除了),除非自己保存下历史contacts。

所以很遗憾,还是没有找到合适的办法。

Pro Android学习笔记(一六十):联系人API(3):联系人数据

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处http://blog.csdn.net/flowingflying/以及作者@恺风Wei。 联系人信息 查看ref...
  • flowingflying
  • flowingflying
  • 2015年03月05日 20:44
  • 5731

联系人Contacts中数据库contacts.db简要分析

主要记录下contact中数据库,视图,索引,触发器的创建,以及在contacts中怎么利用contentprovider操作数据库里表...
  • yin1031468524
  • yin1031468524
  • 2015年12月27日 18:12
  • 2004

关于SQL语句查询最近联系人聊天记录(即最近一条消息)

我想做手机聊天软件的同学经常会遇到这么一个问题,
  • bingshuining
  • bingshuining
  • 2014年05月10日 16:33
  • 1472

openfire-spark 添加联系人默认组修改为中文

openfire功能挺强大的,各种国际化做的挺到位的,但是我们开发的时候往往用不到这么国际化的东西。有时候就需要修改源代码来实现仅支持我们自己的东西。不过这也是懒人的做法。不多说了。理论一大堆。  ...
  • sundenskyqq
  • sundenskyqq
  • 2013年03月26日 15:03
  • 4514

IOS获取系统通讯录联系人信息

IOS关于通讯录的开发有两种,一种是直接调用系统的通讯录界面,根据回调信息处理数据,另一种是直接获取系统的通讯录,完全自定义UI,并且可以通过官方给我们提供的接口进行读写。这篇博客主要讨论第二种方式。...
  • yibaozhifu
  • yibaozhifu
  • 2015年10月20日 11:19
  • 2585

Android内容提供者(一)读取系统联系人

想要读取系统的联系人,必须先获取权限。 1.创建工程ReadContacts,在清单文件中添加读取联系人的权限。2.修改主布局,添加用来显示数据的listview...
  • u014523456
  • u014523456
  • 2016年06月23日 13:56
  • 308

获取手机联系人的两种方法

权限 :   import java.util.ArrayList; import java.util.List; import android.app.Activity; import andr...
  • lixinbb
  • lixinbb
  • 2017年08月02日 10:46
  • 903

用一个简单的办法生成全部排序

有N 个序号:0, 1, 2... N-1其全排序有N!种如何用简单的办法生成全部排序?    static int[] a = ...{0,1,2,3,4,5};    static int n =...
  • rabbitbug
  • rabbitbug
  • 2007年08月15日 17:06
  • 880

根据手机号码查电话薄姓名

TBool CAnsPhoneAppUi::LookupTelNumber(TDes& aName,const TDesC& aNumber){ CContactDatabase* contactsD...
  • utopia2006
  • utopia2006
  • 2008年11月11日 13:56
  • 2839

有没有更好的办法

       转眼过去,从走出学校到现在已经三个月的时间了,这三个月让我从一个刚毕业的学生到职业程序员的转变。      这三个月中我迷茫过,痛苦过,兴奋过,真正的感受到公司员工之间的关系的,也感受到...
  • xiaojun1288
  • xiaojun1288
  • 2010年07月21日 08:16
  • 325
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:有没有办法获取最近操作的联系人
举报原因:
原因补充:

(最多只允许输入30个字)