纠正data表字段描述
data表是存储联系人姓名、号码、邮箱等信息的表。总有疏漏的之后,今天又看了一遍文档才反应过来。
data表实际是没有display_name这一列的,跟raw_contacts表一样。我们之所以无法选择获得display_name这个列是由于底层最终帮我们推动了多表连接查询,就是连接查询data表。具体的查询词语我就不清楚了。不过可以猜个一般:raw_contacts_id等于联系人raw_contacts记录的id且data表的mimetype值为“vnd.android.cursor.item/name”。
通话记录CallLog
通过上面两篇文章的学习,我们终于无法获得到联系人的姓名与号码等信息,今天给大家介绍如何读取和删除通话记录,看懂这三篇文章你就可以自己编码个通讯录软件了。
ContentResolver,对的,今天的主角仍然还是ContentResolver,因为我们只有借助使用ContentResolver对象能够间接的操作底层数据库。如果看过我写的前两篇文章,那么需要对ContentResolver不陌生,至少知道用来干嘛的。如果没用过以及据说过,建议先去查资料学一下android四大模块之一的ContentProvider组件,或者直接搜索ContentResolver的使用。
CallLog,从字面含义理解就是拨打日记(通话记录)吧。其实我们要用到的是它的一个静态内部类Calls,这个外部类Calls定义了一些表述通话记录表的列名。
我并没有去看这个通话记录表的表结构,甚至到目前我连准确的表名都不清楚叫什么,因为我必须用到的字段很简洁,直接从Calls内的常量定义就可以找出。
通话记录表的几个重要的列:
_id:表的字段。基本实现BaseColumns接口的表定义类都是使用“_id”做主键名的,还有一个"_count"必须是记录的数量。type:这条记录的类别。1为呼入查通话记录 指定联系人,2为呼出,3为未接。number:对于呼入类型和未接类型来说,该号码就是给你打电话的人的号码,而针对呼出类型来说,该号码就是你要呼叫的目标对象。date:呼出和呼入的准确时间,这是一个时间戳,需要自己转换。duration:通话时长,单位为秒。对于未接来电来说,这个值仍然为0。
权限准备
想要读取通讯记录其实得要先取到读权限,而即使还想要删除或添加修改就必须再获得写权限。
READ_CALL_LOG:读取通话记录数据库
WRITE_CALL_LOG:写入与删除通话记录
如果是在高版本的android系统电脑上检测还必须添加动态权限判断与获得的代码,我在8.0上早已测试过了。
加载通话记录
编写一个类CallLogLoader,用于封装对通话记录的查询与删除操作。首先CallLogLoader的构造方式需要规定传入一个Context对象,因为我们必须借助Context上下文获取ContentResolver对象,或者你可以直接接受一个ContentResolver对象。
首先是加载通话记录,怎么能够获得到通话记录才是我们如今最关心的。回忆一下之前是如何获得联系人记录的,通过ContentResolver对象执行一条查询语句,而执行一条查询语句必须我们大约提供四个参数:
指定uri想要获取的数组集合选择语句查通话记录 指定联系人,其实叫条件语句更好理解(where),比如"id=?"条件语句必须的参数,参数要对齐条件语句中的“?”占位符位置。
指定要获得的列,对于通话记录,我们只应该关心id、type、number、date、duration这几列就行了。
开始执行一条查询语句。
CursorcalllogCursor=this.mContentResolver.query(android.provider.CallLog.Calls.CONTENT_URI,projection,null,null,android.provider.CallLog.Calls.DEFAULT_SORT_ORDER);
uri使用android.provider.CallLog.Calls.CONTENT_URI。一开始我使用的是android.provider.CallLog.CONTENT_URI,然后就是经常报错,后面我就想试试使用与列名同一个类下的CONTENT_URI试试,没想到还真的是,直接连文档都不用看了。第二个参数就是传我们必须的列的列名集合projection。我今天想查所有的记录,所以条件语句和条件语句附带参数传null,排序使用降序排序。
publicstaticfinalStringDEFAULT_SORT_ORDER="dateDESC";
接着就是读数据了
while(calllogCursor.moveToNext()){//读取每一条记录intid=calllogCursor.getInt(0);inttype=calllogCursor.getInt(1);Stringnumber=calllogCursor.getString(2);Longdate=calllogCursor.getLong(3);intduration=calllogCursor.getInt(4);}calllogCursor.close();
根据号码查询联系人
到现在为止,如果借助判断获得到的通话记录是与哪位联系人的,除非你背得了很多联系人的号码,不然根本不清楚谁谁谁,所以我们需要为每条记录查询出对应的联系人信息。
首先是要按照号码从data表中查询一条存储号码的记录,即mimetype列的值为“vnd.android.cursor.item/phone_v2”的记录。指定要获得的列只要raw_contact_id就可以了,其它字段用不到。
如果找不到,则表明这个号码是未知来源的,这不难理解吧?假如找到了,说明这个号码一定是哪位联系人的,具体是谁的如今还不清楚。但是我们将要获得到的raw_contact_id,根据这个raw_contact_id再从data表中查找mimetype的值为“vnd.android.cursor.item/name”的记录就是这个联系人的姓名了。
到这一步,我们可以获得联系人的信息有:
raw_contact_id:对应raw_contact表的id;display_name:联系人的姓名;
删除通话记录
实现一个删除指定通话记录的方式,通过id编写条件语句删除满足条件的记录。前面删除联系人信息都是使用ContentProviderOperation实现删除操作的,其实直接使用ContentResolver对象的delete方法也可以完成删除,还可以获得到sql语句所影响的行数,即删除成功的函数。而使用ContentProviderOperation的一个弊端就是它支持事务管理,删除失败就回滚操作。
删除操作最重要的就是sql语句的编写,我们只应该提供条件语句就行了,比如我想删除id=1的记录,条件语句就是“id=1”,也可以用占位符“id=?”。而即使我想一次删除多条记录了,那么可以这样写"idin(1,2,4,5,....)",使用占位符就是"idin(?,?,?,?,...)"。
一个好用的功能
后面我们不是实现了查询通话记录和删除通话记录的功能吗,而且在查询通话记录的之后还把通话记录关联的联系人查询出来了,所以目前我想做的功能就是,删除我手机上所有未知来源号码的通话记录。还可以指定只删除呼出的或者只删除呼入的甚至未接的,不指定就是删除所有的。
尾声
详细的代码我就不给了,还是自己动手去实现才好玩,只要能理解前后三篇文章写的内容,你也可以自己动手实现一个通讯录的基本功能了。当然,还有拨打电话的功能没实现,后面我必须实现正则匹配拦截掉一些垃圾号码来电,整天那么多骚扰来电也是烦,又找不到一款合适自己的来电拦截软件,所以就只好自己动手了。通过修改正则表达式拦截匹配成功的号码的来电。