最近因为项目需要,研究了Android联系人相关内容,包括联系人数据库,获取联系人数据,使用ListView展示联系人。我将按照以下几点记录:
Android存储联系人数据库表结构
获取联系人数据
联系人列表效果
一Android存储联系人数据库表结构
要想搞清楚Android联系人内容,首先就得清楚这些内容在Android中是怎么存储的,为了搞清楚这个问题,可以直接取出Android中存储联系人的数据库contact2.db。它的存储路径:
/data/data/com.android.providers.contacts/databases.contacts2.db
如果是模拟器,可以直接取出来。如果是真机的话需要获取root权限。取出之后用可以查看SQLite数据库的软件(我用的是SQLiteSpy)打开,结构如下:
这里包括了很多表,而我们只看几个比较重要的表,contacts,data,mimetypes,raw_contacts。
contacts表
该表保存了所有的手机联系人,包括每个联系人id、联系次数(times_contacted)、最后一次联系的时间(last_time_contactde)、是否含有电话号码(has_phone_number)、是否被收藏(starred)等信息。来看看以下字段:
_id :表id,主要用于其他表通过该字段来查找相应的数据。
lookup:该字段是不会改变的,联系人的信息可能改变,但是该字段是一直存储不会改变的。
name_raw_contact_id:这个字段对于我们暂时没有什么作用,先忽略他。
raw_contacts表
该表中的字段比较多,也包含了大量的信息,对于我们实现联系人列表有很大的作用,这里我只调几个字段来看看,其他的可以自己查看:
_id:其他表如contacts,data表可以通过raw_contact_id来查找raw_contacts表中的数据。
contact_id:通过该字段raw_contacts表就可以去查找contacts表,这样两张表就联系起来了。
display_name(display_name_alt):联系人姓名,不用多说。
sort_key:我们在取数据时可以安装该字段来排序。这样我们取到的数据就是排好顺序的。
phone_book_label:联系人首字母,我们可以取得该字段来实现类似微信通讯录联系人的效果。
deleted:该联系人是否被删除。
mimetypes表
这个表很有意思也很重要,他主要是定义了联系人的数据类型,如果我们想自己定义一个联系人属性需要在这个表中添加,例如:我想扩展添加一个微信号,我们可以这样vnd.android.cursor.item/weichat,可以存储微信号等属性,有兴趣的可以去自己实现一下。
data表
该表保存了所有创建过的手机联系人的信息保存在列data1至data15中,该表保存了两个ID:mimetype_id和raw_contact_id,从而将data表和
raw_contacts表,mimetypes表联系起来了。在看看mimetype_id这个字段代表是什么意思呢,看上图就明白了,他其实代表的是该行的数据类型,下图中第一行mimetype_id 为1,那么对应到mimetypes表中是vnd.android.cursor.item/phone_v2,表示改行数据是电话号码。接着看data1就是号码,data2是号码类型(住宅,手机,座机等等)。改表也是可以扩展的。
以上几个表都是比较重要的,从这几个表中我们基本上可以满足我们做联系人列表的需求了。这里还要说一下,可能细心一点的话,会发觉有些不同表中有同一字段,这是不是违背了数据库设计规范造成数据冗余呢,Google不可能想不到这个问题,这样做主要是为了用成本较低的存储空间,去换取在表中联合查询时减少的时间。用空间换时间是不错的选择。
到这儿,已经对联系人数据库有了初步的认识,接下来我们就来看看怎么获取这些表中的数据。
获取联系人数据
Android4.0之后在android.provider包下有一个ContactsContract类,用来管理联系人信息。该类结构比较复杂,有三个比较重要的内部类ConstractContact.Data,ConstractContact.RawContacts,ConstractContact.Contacts。这三个类实际上就是对应着上边介绍的data,raw_contacts,contacts三张表。
在ConstractContact.Phone中有个 Phone.CONTENT_URI字段,看源码可以知道他指向的是“content:// com.android.contacts/data/phones”,而这个url实际上对应这data,contacts,raw_contacts,这三个表。再一次证明我们的联系人数据就是从三张表取的。
来看看具体代码,我们取出联系人的姓名和电话号码,并安装首字母排序
private static final String PHONE_BOOK_LABLE="phonebook_label";
/**需要查询的字段**/
private static final String[]PHONES_PROJECTION={Phone.DISPLAY_NAME
,Phone.NUMBER,PHONE_BOOK_LABLE};
/**联系人显示名称**/
private static final int PHONES_DISPLAY_NAME_INDEX = 0;
/**电话号码**/
private static final int PHONES_NUMBER_INDEX = 1;
ContentResolver mResolver=getContentResolver();
//查询联系人数据,query的参数Phone.SORT_KEY_PRIMARY表示将结果集按Phone.SORT_KEY_PRIMARY排序
Cursor cursor=mResolver.query(Phone.CONTENT_URI
,PHONES_PROJECTION,null,null,Phone.SORT_KEY_PRIMARY);
if(cursor!=null){
while (cursor.moveToNext()){
ContactsModel model=new ContactsModel();
model.setPhone(cursor.getString(PHONES_NUMBER_INDEX));
if(TextUtils.isEmpty(model.getPhone())){
continue;
}
model.setName(cursor.getString(PHONES_DISPLAY_NAME_INDEX));
model.setPhonebook_label(cursor.getString(cursor.getColumnIndex(PHONE_BOOK_LABLE)));
contactsModelList.add(model);
}
cursor.close();
}
来看看以下字段phonebook_label,就是在raw_contact表中,存储的是首字母。我可以用他来作为分组的label
private static final String PHONE_BOOK_LABLE="phonebook_label";
该数组是我们需要查询字段的集合,需要查询什么字段,直接在该数组中添加就ok了。
/**需要查询的字段**/
private static final String[]PHONES_PROJECTION={Phone.DISPLAY_NAME
,Phone.NUMBER,PHONE_BOOK_LABLE};
为了方便,我直接在取数据时就以Phone.SORT_KEY_PRIMARY(实际上就是“sort_key”字段)排好序了。这样我们就取得了联系人数据了。这里我们定义一下联系人model:
/**
* Created by Administrator on 2015/11/23.
*/
public class ContactsModel {
private String name;
private String phone;
private String phonebook_label;
public String getPhonebook_label() {
return phonebook_label;
}
public void setPhonebook_label(String phonebook_label) {
this.phonebook_label = phonebook_label;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
还有一个非常重要的就是需要添加读取联系人权限(如果需要增删改查还需要写权限)。在清单文件AndroidManifest.xml中添加:
<