在Android开发中,读取通讯录算是一个比较常用的功能,很多app都会读取你的通讯录做一些匹配,或者从通讯录邀请好友等。虽然Android系统提供了接口让你读取通讯录,但在使用的过程中并不方便,因为需要你自己通过API去从数据库的表里面读。既然读取通讯录的这个功能这么常用,我们不妨封装一下。
在封装之前,我们先简单介绍一下Android系统中对联系人的存储方式。android系统中联系人的姓名和电话其实是分开存储的,不知道为什么要这么设计。在两个表当中,他们都有一个ID字段,如果知道了对应的ID,我们就能查到联系人的姓名和电话。
ContactsContract.CommonDataKinds.Phone.CONTENT_URI表中存储的电话号码字段,我们可以通过如下查询语句来获得通讯录中所有联系人的电话号码和ID字段:
Cursor phoneNumberCursor;
String[] phoneProjection = {ContactsContract.CommonDataKinds.Phone.CONTACT_ID, ContactsContract.CommonDataKinds.Phone.NUMBER};
phoneNumberCursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, phoneProjection, null, null, null);
ContactsContract.Contacts.CONTENT_URI表中存储的是联系人的姓名,通过下面的查询来获得联系人的姓名:
Cursor nameCursor;
String[] nameProjection = {ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME};
nameCursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, nameProjection, null, null, null);
两个表靠_id字段关联,这样我们就可以利用两个表的信息来得到联系人的姓名和电话号码。
为了使用方便,我们可以定义联系人对象的数据结构,最常用的是姓名和电话字段,所以我们暂时只添加这两个字段:
class PersonInfo{
String name;
String phone;
}
根据定义好的联系人对象,从两个表中读取对应的字段构建PersonInfo对象
//正则表达式,判断是否是手机号
private boolean isMobilePhoneNumber(String phoneNumber) {
Pattern pattern = Pattern.compile("^[1][3,4,5,7,8][0-9]{9}$");
Matcher matcher = pattern.matcher(phoneNumber);
return matcher.matches();
}
//格式化电话号码结构
private String phoneNumberFormat(String rawPhoneNumber) {
if (rawPhoneNumber == null || rawPhoneNumber.length() == 0 || rawPhoneNumber.length() < 11)
return rawPhoneNumber;
if (rawPhoneNumber.contains("-")) {
rawPhoneNumber = rawPhoneNumber.replace("-", "");
}
if (rawPhoneNumber.contains(" ")) {
rawPhoneNumber = rawPhoneNumber.replace(" ", "");
}
if (rawPhoneNumber.startsWith("+86")) {
rawPhoneNumber = rawPhoneNumber.substring(3, rawPhoneNumber.length());
}
return rawPhoneNumber;
}
//读取联系人电话号码
private void getPhoneNumList() {
Cursor phoneNumberCursor;
String[] phoneProjection = {ContactsContract.CommonDataKinds.Phone.CONTACT_ID, ContactsContract.CommonDataKinds.Phone.NUMBER};
phoneNumberCursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, phoneProjection, null, null, null);
if (phoneNumberCursor == null) {
return;
}
if (phoneNumberCursor.getCount() == 0) {
phoneNumberCursor.close();
return;
}
if (phoneNumberCursor.moveToFirst()) {
int idIndex = phoneNumberCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID);
int phoneNumIndex = phoneNumberCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
do {
String idValue = phoneNumberCursor.getString(idIndex);
String phoneNumValue = phoneNumberCursor.getString(phoneNumIndex);
String processedNumValue = phoneNumberFormat(phoneNumValue);
if (isMobilePhoneNumber(processedNumValue)) {
PersonInfo personInfo = new PersonInfo();
personInfo.setId(idValue);
personInfo.setPhoneNumber(processedNumValue);
personInfoArrayList.add(personInfo);
}
} while (phoneNumberCursor.moveToNext());
if (!phoneNumberCursor.isClosed()) {
phoneNumberCursor.close();
}
}
}
//读取联系人姓名
private void buildPersonInfo() {
Cursor nameCursor;
String[] nameProjection = {ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME};
nameCursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, nameProjection, null, null, null);
if (nameCursor == null) {
return;
}
if (nameCursor.getCount() == 0) {
nameCursor.close();
return;
}
int _idIndex = nameCursor.getColumnIndex(ContactsContract.Contacts._ID);
int _nameIndex = nameCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
for (PersonInfo obj : personInfoArrayList) {
if (nameCursor.moveToFirst()) {
do {
String _id = nameCursor.getString(_idIndex);
String _name = nameCursor.getString(_nameIndex);
if (obj.getId().equals(_id)) {
obj.setName(_name);
}
} while (nameCursor.moveToNext());
}
}
nameCursor.close();
}
通过上面两个方法,我们就可以得到构建好的联系人列表,在处理的过程中,我们针对读取出来的手机号码进行了简单的格式化,去掉手机号中除了数字之外的不必要字符,同时针对处理后的号码进行正则匹配,保证读取出来的都是手机号码。
核心的代码就是从两个表中读取对应的信息,然后进行组合。通讯录的模块比较常用,我们可以将上述实现封装成一个类,对外只提供一个接口,直接返回personInfoArrayList对象。