最近给别人做android的项目,其中有关于手机联系人的那一块,要求查看联系人的信息(手机号码,名字,所在群组,家庭号码,家庭地址,电子邮箱,备注,公司,工作号码等)。在此分享下学习android手机联系人数据库的知识。如有遗漏和错误,望请教。

   运行结果图如下:

184243283.png

  上面是每个item对应的布局,右下角被挡住的两个分别为家庭号码,工作号码

201515587.png

   哎,不同人信息不一样,长度也不一样啊,看起来不是很美观,j_0021.gif

在群组中,因为所有人都在“所有联系人”组中,又在其他组中,故:查询组有多个。


   还是先把核心代码贴出来

private void getData(){
        HashMap<String, Item> list = new HashMap<String, Item>(100);
        /*Cursor c = getContentResolver().query(Contacts.CONTENT_URI, new String[]{Contacts._ID}, null, null, null);
        if(c.moveToFirst()){
            int id = c.getColumnIndex(Contacts._ID);
        }*/
        // 姓名和手机号
        Cursor c = getContentResolver().query(Phone.CONTENT_URI,
                new String[]{Phone.DISPLAY_NAME, Phone.NUMBER, Phone.CONTACT_ID},
                Phone.TYPE + "=?",
                new String[]{Phone.TYPE_MOBILE+""}, null);
        if(c.moveToFirst()){
            int id = c.getColumnIndex(Phone.CONTACT_ID);
            int name = c.getColumnIndex(Phone.DISPLAY_NAME);
            int num = c.getColumnIndex(Phone.NUMBER);
            do{
                String cid = c.getString(id);
                String cname = c.getString(name);
                String cnum = c.getString(num);
                Item remark = list.get(cid);
                if(remark == null){
                    remark = new  Item();
                    list.put(cid, remark);
                }
                remark.name = cname;
                remark.number = cnum;
                Log.i(TAG, "name:" + cname + " num:" + cnum);
            }while(c.moveToNext());
        }
        c.close();
        //  邮箱信息
        c = getContentResolver().query(Email.CONTENT_URI,
                new String[]{Email.DATA, Email.CONTACT_ID},
                null, null, Email.CONTACT_ID + " asc");
        if(c.moveToFirst()){
            int id = c.getColumnIndex(Email.CONTACT_ID);
            int em = c.getColumnIndex(Email.DATA);
            do{
                String cid = c.getString(id);
                String email = c.getString(em);
                Item remark = list.get(cid);
                if(remark == null){
                    remark = new Item();
                    list.put(cid, remark);
                }
                remark.email = email;
                Log.i(TAG, "email:" + email);
            }while(c.moveToNext());
        }
        c.close();
        Log.i(TAG, "start 工作号码");
        // 工作号码
        String[] projection = new String[] { Phone.NUMBER, Phone.CONTACT_ID};
        c = getContentResolver().query(
                Phone.CONTENT_URI, projection,
                Phone.TYPE + "=?",
                new String[]{Phone.TYPE_WORK + ""},
                Phone.CONTACT_ID + " asc");
        if(c.moveToFirst()){
            int num = c.getColumnIndex(Phone.NUMBER);
            int id = c.getColumnIndex(Phone.CONTACT_ID);
            do{
                String cid = c.getString(id);
                String phone = c.getString(num);
                Item remark = list.get(cid);
                if(remark == null){
                    remark = new Item();
                    list.put(cid, remark);
                }
                remark.workTel = phone;
                Log.i(TAG, "workTel:" + phone);
            }while(c.moveToNext());
        }
        c.close();
        // 公司名字
        c = getContentResolver().query(
                Data.CONTENT_URI, new String[]{Data.DATA1, Data.CONTACT_ID},
                Data.MIMETYPE + "=?",
                new String[]{Organization.CONTENT_ITEM_TYPE},
                Data.CONTACT_ID + " asc");
        if(c.moveToFirst()){
            int data1 = c.getColumnIndex(Data.DATA1);
            int id = c.getColumnIndex(Data.CONTACT_ID);
            do{
                String cid = c.getString(id);
                String companyname = c.getString(data1);
                Item remark = list.get(cid);
                if(remark == null){
                    remark = new Item();
                    list.put(cid, remark);
                }
                remark.company = companyname;
                Log.i(TAG, "company:" + companyname);
            }while(c.moveToNext());
        }
        c.close(); c = null;
        // 家庭住址信息
        projection = new String[] { Data.DATA7,
                Data.DATA10, Data.DATA4, Data.CONTACT_ID };
        c = getContentResolver().query(
                Data.CONTENT_URI, projection,
                Data.MIMETYPE + "=? and " + StructuredPostal.TYPE + "=?" ,
                new String[]{StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE_HOME+""},
                Data.CONTACT_ID + " asc");
        if(c.moveToFirst()){
            int data4 = c.getColumnIndex(Data.DATA4);
            int data7 = c.getColumnIndex(Data.DATA7);
            int data10 = c.getColumnIndex(Data.DATA10);
            int id = c.getColumnIndex(Data.CONTACT_ID);
            do{
                String sData4 = c.getString(data4);
                String sData7 = c.getString(data7);
                String sData10 = c.getString(data10);
                String cid = c.getString(id);
                StringBuilder sb = new StringBuilder();
                // 依次为国家、城市、街道
                if(!TextUtils.isEmpty(sData10))
                    sb.append(sData10).append(";");
                if(!TextUtils.isEmpty(sData7))
                    sb.append(sData7).append(";");
                if(!TextUtils.isEmpty(sData4))
                    sb.append(sData4);
                Item remark = list.get(cid);
                if(remark == null){
                    remark = new Item();
                    list.put(cid, remark);
                }
                remark.address = sb.toString();
                Log.i(TAG, "address:" + remark.address);
            }while(c.moveToNext());
        }
        c.close();
        // 家庭号码
        projection = new String[] { Phone.NUMBER, Phone.CONTACT_ID};
        c = getContentResolver().query(
                Phone.CONTENT_URI, projection,
                Phone.TYPE + "=?",
                new String[]{Phone.TYPE_HOME + ""},
                Phone.CONTACT_ID + " asc");
        if(c.moveToFirst()){
            int num = c.getColumnIndex(Phone.NUMBER);
            int id = c.getColumnIndex(Phone.CONTACT_ID);
            do{
                String cid = c.getString(id);
                String phone = c.getString(num);
                Item remark = list.get(cid);
                if(remark == null){
                    remark = new Item();
                    list.put(cid, remark);
                }
                remark.fnumber = phone;
                Log.i(TAG, "family Phone:" + phone);
            }while(c.moveToNext());
        }
        c.close();
        // 备注
        projection = new String[] { Data.DATA1, Data.CONTACT_ID};
        c = getContentResolver().query(
                Data.CONTENT_URI,
                projection,
                Data.MIMETYPE + "=?",
                new String[]{Note.CONTENT_ITEM_TYPE},
                Data.CONTACT_ID + " asc");
        if(c.moveToFirst()){
            int data1 = c.getColumnIndex(Data.DATA1);
            int id = c.getColumnIndex(Data.CONTACT_ID);
            do{
                String cid = c.getString(id);
                String note = c.getString(data1);
                Item remark = list.get(cid);
                if(remark == null){
                    remark = new Item();
                    list.put(cid, remark);
                }
                if(!TextUtils.isEmpty(note)){
                    remark.note = note;
                    Log.i(TAG, "note:" + note);
                }
            }while(c.moveToNext());
        }
        c.close();
        // 对应的分组
        ArrayList<String> gid = new ArrayList<String>();    // group Id
        ArrayList<String> gname = new ArrayList<String>();  // group name
        c = getContentResolver().query(Groups.CONTENT_URI,
                new String[]{Groups.TITLE, Groups._ID}, null, null, null);
        if(c.moveToFirst()){
            int cc1 = c.getColumnIndex(Groups._ID);
            int cc2 = c.getColumnIndex(Groups.TITLE);
            do{
                String id = c.getString(cc1);
                String ti = c.getString(cc2);
                gid.add(id);
                gname.add(ti);
                Log.i(TAG, ti);
            }while(c.moveToNext());
        }
        c.close();
        int len = gid.size(); /// 总共有多少个群组
        c = getContentResolver().query(Data.CONTENT_URI,
                new String[]{GroupMembership.CONTACT_ID, GroupMembership.GROUP_ROW_ID},
                Data.MIMETYPE + "=?",
                new String[]{GroupMembership.CONTENT_ITEM_TYPE}, null);
        if(c.moveToFirst()){
            int cc1 = c.getColumnIndex(GroupMembership.CONTACT_ID);
            int cc2 = c.getColumnIndex(GroupMembership.GROUP_ROW_ID);// group ID
            do{
                String id = c.getString(cc1);
                String groupId = c.getString(cc2);
                Item remark = list.get(id);
                if(remark == null){
                    remark = new Item();
                    list.put(id, remark);
                }
                for(int i = 0; i < len; i++){
                    if(gid.get(i).equals(groupId)){
                        remark.group += gname.get(i)+" ";// 一个人可能有多个群组
                    }
                }
            }while(c.moveToNext());
        }
        c.close();
        gid = null;
        gname = null;
        Log.i(TAG, "end");
        if(data.list.size() == 0)
            data.list.clear();
        Collection<Item> vs = list.values();
        for(Item it : vs)
            data.list.add(it);
        Log.i(TAG, "size" + data.list.size());
    }


class ViewHolder {
        TextView name, note;
        TextView number, address;
        TextView group, company;
        TextView email, workTel;
        TextView fnumber;
    }
                                                                                                                                                                                                                                                                                                                                                                         
    class Item {
        String name;        // 姓名
        String number;      // 电话号码
        String group="";        // 群组
        String note;        // 备注
        String address;     // 家庭地址
        String fnumber;     // 家庭号码
        String company;     // 所在公司
        String workTel;     // 工作号码
        String email;       // 邮箱
    }

   group = "" ,是因为某些人可能属于多个分组,为了在拼接字符串时不会有null所以赋予""


在进行一系列查询的时候应该注意一点的就是:Contacts表中_ID是唯一的,也是和其他表关联的,也就是说,得到这个Id后,其他信息都可以通过它得到。那么反过来利用其他值(如:手机号)来得到这个id后,也就可以得到其他相关的信息了。

   为什么要注释掉在联系人表中的查询?

   其中一个原因就是可能会有冗余的信息(某个人被删了,但他的一些信息却是存在的),这与android手机联系人数据库表设计有关。因为在删除一个人的时候,并不是直接删除了,而是做标记代替的。

   出于上面的原因,所以没有从这表中开始查询。

   另外在系统中有一张总表android.provider.ContactsContract.Data。在这个表里面存放着所有联系人的所有具体信息(只要设置条件可以代替其他表做查询)。


在本例中为了使联系人的信息是对应的,用HashMap来保存,contact_id作为key,将九种信息封装成类作为HashMap的value

   现在分析代码中取得的九种不同信息

   1、手机号表Open Declarationandroid.provider.ContactsContract.CommonDataKinds.Phone)

在这张表中,可以找到联系人名,以及各种号码(手机号,家庭号码,工作号码等)等

  在Phone中有一个属性叫做TYPE,它是用来标识号码类型的。以上三种类型分别对应的是:

  TYPE_MOBILE,TYPE_HOME, TYPE_WORK.

      此时查询函数使用如下

Cursor c = getContentResolver().query(Phone.CONTENT_URI,
                new String[]{Phone.DISPLAY_NAME, Phone.NUMBER, Phone.CONTACT_ID},
                Phone.TYPE + "=?",
                new String[]{Phone.TYPE_MOBILE+""}, null);

      在where语句中用到了TYPE来限制取的是哪一种类型的号码。同理可以取得家庭号码工作号码

j_0061.gif这样就解决了四种信息(姓名,三种号码)的获取了


  2、邮箱表Open Declarationandroid.provider.ContactsContract.CommonDataKinds.Email)

     和Phone表一样,Email类也有一个TYPE属性来标识邮箱类:

  TYPE_HOME,TYPE_MOBILE,TYPE_WORK.意思很直观,就不解释了。

      此时查询函数使用如下:

getContentResolver().query(Email.CONTENT_URI,
                new String[]{Email.DATA, Email.CONTACT_ID},
                null, null, null);

      在代码中,我没有去区分到底是什么类型的邮箱。如果要区分,就在where中用type限制吧。


   3、组织表(android.provider.ContactsContract.CommonDataKinds.Organization)

      把它叫做organization应该是有原因的吧,公司是可以看做一种组织的。与前面查询不同的是Organization类没有CONTENT_URI的属性,也就是说,不能直接查。

      在Organization中可以查到的信息有公司名、职位、办公位置等。

      前面说过在Data中有所有联系人相关的信息,那么此时应该查询Data,通过限制查询Organization的信息。

       此时查询函数使用如下:

getContentResolver().query(
                Data.CONTENT_URI, new String[]{Data.DATA1, Data.CONTACT_ID},
                Data.MIMETYPE + "=?",
                new String[]{Organization.CONTENT_ITEM_TYPE},null);

       在查询映射字段中Data.DATA1其实对应的就是Organization中的COMPANY。所以换成Organization.COMPANY也可以。至于这些对应关系可以在android开发文档中查看。


    4、地址信息(android.provider.ContactsContract.CommonDataKinds.StructuredPostal)

       在这里,可以查到所在国家,城市,街道,邮编,区域等等

       在前面查询公司时,还没有说怎么查询公司所在地。j_0057.gif,在StructurePost中有一个字段TYPE,他对应的有以下几种类型TYPE_CUSTOM,TYPE_HOME,TYPE_WORK,TYPE_OTHER。意思很明确就不说了。

       那怎么查询呢?遗憾的是StrucurePost中没有CONTENT_URI,所以我们不能直接查,所幸的是我们又可以利用Data来查询。如下查询家庭地址

projection = new String[] { Data.DATA7,
                Data.DATA10, Data.DATA4, Data.CONTACT_ID };
        c = getContentResolver().query(
                Data.CONTENT_URI, projection,
                Data.MIMETYPE + "=? and " + StructuredPostal.TYPE + "=?" ,
                new String[]{StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE_HOME+""},
                null);

      DATA10,DATA7,DATA4分别对应国家,城市,街道(对应的COUNTRY,CITY,STREET)

      其他对应关系请看android 开发文档


     5、备注(android.provider.ContactsContract.CommonDataKinds.Note

        也是利用Data查询

projection = new String[] { Data.DATA1, Data.CONTACT_ID};
        c = getContentResolver().query(
                Data.CONTENT_URI,
                projection,
                Data.MIMETYPE + "=?",
                new String[]{Note.CONTENT_ITEM_TYPE},
                null);


     6、联系人所在群组查询稍微麻烦一点点啦

           android.provider.ContactsContract.Groups

android.provider.ContactsContract.CommonDataKinds.GroupMembership

        在Groups中存放的是手机系统当前的所有分组信息。

GroupMembership中关联了联系人所在群组。

第一步:应该取出当前手机中的所有分组信息

c = getContentResolver().query(Groups.CONTENT_URI,
                new String[]{Groups.TITLE, Groups._ID}, null, null, null);

        通过GroupMembership.GROUP_ROW_ID关联到Groups._ID

第二步:通过Data查询Groupmembership的信息

getContentResolver().query(Data.CONTENT_URI,
                new String[]{GroupMembership.CONTACT_ID, GroupMembership.GROUP_ROW_ID},
                Data.MIMETYPE + "=?",
                new String[]{GroupMembership.CONTENT_ITEM_TYPE}, null);

      故:在查询分组信息时,用了两个临时List来存放Groups信息


j_0020.gif ok了,所有查询可以完美解决啦。



哈哈!!!通过这次实践对android手机联系人数据库有一定了解了。

   A:每个联系人信息都被唯一的一个contact_id来标识

   B:大表(Data)细分为小表(Phone等)

   C:数据块化(Note,StrucuredPostal等)

   D:数据类型化(Phone的几种类,Email的几种类型等等)


个人觉得掌握这几点就可以查出android手机联系人中所有联系人具体信息了。当然,这些信息你在添加联系人的时候要添加了才有,不然还是null


   在分析中也阐述了查询其他信息的做法,希望对大家有用!!!