安卓手机应用获取通讯录中的联系人

前语:真机运行正常,在适配数据时可优化加载性能。

第 1 步 了解读取联系人数据的原理

a.手机联系人数据库的存放路径:
data/data/com.android.providers.contacts/databases/contacts2.db
这里写图片描述

b.这个数据库中涉及3张表:raw_contacts、data、mimetypes,其中:

raw_contacts:可以读取联系人的contact_id。
data:可以根据联系人的contact_id,读到联系人的具体内容如姓名、电话、mimtype_id和raw_contacts_id。
mimetypes:可以读到这个联系人的具体内容是什么类型的,比如是email、电话等等。

c.raw_contacts表中的字段contact_id就是联系人的ID。用SQLite工具打开contacts2.db,找到raw_contacts表格,这张表中的字段contact_id就是联系人的ID,系统会给每个人分配一个ID。这张表只是联系人信息的简单摘要,没有详细信息,
这里写图片描述

d. data表中的raw_contacts_id就是contact_id。打开data表格,联系人的详细信息就在data表格中,但是这张表的数据很多,联系人的信息(姓名和电话)被记录在同一列,有时候这一列还可能有很多脏数据(空记录)。
这里写图片描述

e. data表中的data1数据是需要根据mimtype_id的整数值来分类匹配,这个整数值是由mimtypes表中的定义来决定的。
这里写图片描述

f. 在查系统的通讯录时,系统的通讯录提供了一个contentprovider,供其他的应用来调用联系人数据,我们需要用内容解析者来获取getcontentResolver()和查询query()它所提供的数据;

/**翻译的API内容
* 查询给定的URI,返回结果集上的游标。 为了获得最佳性能,调用者应遵循这些准则:
     * 1.提供一个明确的projection,防止从存储中读取不需要的数据。
     * 2.使用问号标记参数,如“phone=?”而不是选择参数中的传一个明确的值,因为对于缓存来说,这种方式和通过指定的值来查询的效果会被识别为相同。

* 参数: 
     * URI,使用context://scheme,为内容检索。这个URL由内容提供者定义的字符串,scheme为主机名 
     * projection 根据明确的projection字段,返回指定列的列表。传递NULL将返回所有列,这是效率很低的。 
     * selection 声明需要返回的那一行的过滤器,格式化为一个SQL WHERE条件(不包括条件语句本身)。根据给定的URI传递null进去将会返回的所有行。
     * selectionArgs 在selection中可能包括许多"?",他将会被selectionArgs中的值替换,以便在它们在selection中出现。这些值将会限制为字符串。 
     * sortOrder 如何对这行进行排序,通过SQL ORDER BY条件命令(不包括这句命令本身)。传递NULL将使用默认排序顺序,这也有可能是无序的排序。 
* 返回值:
     *  Cursor 游标/光标对象,它位于第一个条目之前,或者返回一个空对象
     */
    // 通过内容解析者,来查询手机联系人数据库里的信息。参数说明如上。
    getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);

g. query中uri 的获取。
(1)URI 中主机名的获取:
系统的联系人主机名可以从源码中获取,可以参考系统是如何在清单文件(AndroidManifest.xml)中配置主机名的,路径:
\packages\packages\provider\platform_packages_providers_contactsprovider-master\platform_packages_providers_contactsprovider-master
如下,主机名是com.android.contacts,但主机名只能定位到这个数据库,而不能定位到raw_contacts这张表。

<provider android:name="ContactsProvider2"
            android:authorities="contacts;com.android.contacts"<!--主机名-->
            android:label="@string/provider_label"
            android:multiprocess="false"
            android:exported="true"
            android:readPermission="android.permission.READ_CONTACTS"
            android:writePermission="android.permission.WRITE_CONTACTS">
            <path-permission
                    android:pathPrefix="/search_suggest_query"
                    android:readPermission="android.permission.GLOBAL_SEARCH" />
            <path-permission
                    android:pathPrefix="/search_suggest_shortcut"
                    android:readPermission="android.permission.GLOBAL_SEARCH" />
            <path-permission
                    android:pathPattern="/contacts/.*/photo"
                    android:readPermission="android.permission.GLOBAL_SEARCH" />
            <grant-uri-permission android:pathPattern=".*" />
        </provider>

(2)使uri定位能到raw_contacts表,可以参考源码(contactsprovider2.java)路径:
\packages\packages\provider\platform_packages_providers_contactsprovider-master\platform_packages_providers_contactsprovider-master\src\com\android\providers\contacts\contactsprovider2.java
找到下面代码:

 //-------------------主机名-----------------raw_contacts---------------
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);

(3)拼接和解析Uri。

Uri.parse("content://com.android.contacts/raw_contacts")         

第 2 步: 写好一个ListView,准备好显示联系人的View
(1)准备好ListView布局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/lv_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

(2)为ListView填充item

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="姓名"
         android:textColor="#000000"/>
    <TextView
        android:id="@+id/tv_phoneNum"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="电话"
         android:textColor="#000000" />
</LinearLayout>

(3)给ListView填充数据item,适配数据。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ListView listview = (ListView) findViewById(R.id.lv_list);
        // 将下面的第3步中获取的数据保存到一个list集合中
        ArrayList<HashMap<String, String>> contacts = readContacts();
        // 给ListView适配数据(在这里可优化加载性能)
        listview.setAdapter(new SimpleAdapter(this, contacts,
                R.layout.list_item_contacts,
                new String[] { "name", "phoneNum" }, new int[] { R.id.tv_name,
                        R.id.tv_phoneNum }));
    }

第3步 遍历查询到的数据
data这张表有一个视图view_data, 这个视图也叫虚表,可以把两个不相关的实表中的数据(data表和mimetypes表)整合成一张虚表,当我们查询data表时,实际上系统查询的是视图view_data,所以在查询语句new String[] { “data1”, “mimetype” },而没有用mimetype_id。
这里写图片描述

private ArrayList<HashMap<String, String>> readContacts() {
        // 用解析者查询到联系人表,返回一个数据库的Cursor对象,他里面有我们关心的数据。
        Cursor rawCursor = getContentResolver().query(              Uri.parse("content://com.android.contacts/raw_contacts"),
                new String[] { "contact_id" }, null, null, null);

        //用list集合来保存数据一组hashMap对象,即一组联系人信息
        ArrayList<HashMap<String, String>> arrayList = new ArrayList<HashMap<String, String>>();

        while (rawCursor.moveToNext()) {
            String contactsID = rawCursor.getString(0);
            //查询data表,从data表中得到含有data1和mimetype数据的cursor对象
            Cursor dataCursor = getContentResolver().query(
                    Uri.parse("content://com.android.contacts/data"),
                    new String[] { "data1", "mimetype" },
                    "raw_contact_id=?", new String[] { contactsID }, null);

            //用HashMap来保存一条联系人的信息姓名和电话数据
            HashMap<String, String> hashMap = new HashMap<String, String>();

            //从dataCursor中获取表中的内容,0代表data1数据,1代表类别。也可以用dataCursor.getString(dataCursor.getColumnIndex("name"))直接获取相关数据。
            while(dataCursor.moveToNext()){
                String values= dataCursor.getString(0);
                String types = dataCursor.getString(1);
                //根据类型区分姓名和电话号码,并保存到Map中
                if("vnd.android.cursor.item/name".equals(types)){
                    hashMap.put("name", values);
                }else if("vnd.android.cursor.item/phone_v2".equals(types)){
                    hashMap.put("phoneNum", values);
                }
            }
            dataCursor.close();
            arrayList.add(hashMap);//在list集合中保存Map对象,Map对象中含有联系人的姓名和电话
                    }
        rawCursor.close();
        return arrayList;
    }

注:
1. 在真机上运行上述代码,如果联系人太多或者手机性能较差会导致这个方法运行缓慢,会造成主线程阻塞几秒,超过5秒会造成ANR,一般在子线程中将数据读取完成后,用Handler发消息通知再适配数据(setAdapter)。
2. 真机运行时会出现许多空白item,因为真机的数据库里有很多脏数据。在用list集合保存时可以简单过滤一下。

   if(!TextUtils.isEmpty(hashMap.get("name")) && !TextUtils.isEmpty(hashMap.get("phoneNum"))){
                //过滤脏数据
                arrayList.add(hashMap);
            }

最后运行的结果:
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值