You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在API17版本的Android应用中读取联系人数据

嘿,完全不用担心!API 17读取联系人数据其实比API23+更简单——因为在Android 6.0(API23)之前,权限是安装时直接授予的,不需要做运行时权限申请那套流程,实现起来反而更直接。下面给你一步步拆解具体做法:

实现步骤

1. 先在Manifest里添加权限

打开你的AndroidManifest.xml,在<application>标签外面加上读取联系人的权限:

<uses-permission android:name="android.permission.READ_CONTACTS" />

对API17来说,安装应用后这个权限就自动生效了,不用额外做动态申请。

2. 读取联系人数据的两种常用方法

方法一:直接用ContentResolver同步查询(注意不要在主线程跑)

这种方式代码直接,但因为是同步操作,绝对不能在UI线程调用(会导致ANR),建议放在AsyncTask或者单独的线程里执行。示例代码如下:

private void fetchContacts() {
    ContentResolver contentResolver = getContentResolver();
    // 查询联系人主表的所有数据
    Cursor contactCursor = contentResolver.query(
            ContactsContract.Contacts.CONTENT_URI,
            null,
            null,
            null,
            null);

    if (contactCursor != null && contactCursor.moveToFirst()) {
        do {
            // 获取联系人ID和姓名
            String contactId = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID));
            String contactName = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));

            // 根据联系人ID查询对应的电话号码(一个联系人可能有多个号码)
            Cursor phoneCursor = contentResolver.query(
                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                    null,
                    ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
                    new String[]{contactId},
                    null);

            if (phoneCursor != null) {
                while (phoneCursor.moveToNext()) {
                    String phoneNumber = phoneCursor.getString(
                            phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                    // 这里可以把姓名和号码存到列表里,或者做其他处理
                    Log.d("ContactInfo", "姓名:" + contactName + ",电话:" + phoneNumber);
                }
                phoneCursor.close(); // 用完记得关闭Cursor,避免内存泄漏
            }
        } while (contactCursor.moveToNext());
        contactCursor.close();
    }
}

方法二:用CursorLoader异步查询(推荐,更符合Android最佳实践)

CursorLoader会在后台线程处理查询操作,不会阻塞UI,还能自动管理Cursor的生命周期,适合和UI组件(比如ListView/RecyclerView)配合使用。步骤如下:

  1. 让你的Activity/Fragment实现LoaderManager.LoaderCallbacks<Cursor>接口
  2. 在onCreate里初始化Loader
  3. 实现三个回调方法处理查询逻辑

示例代码:

public class ContactListActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact_list);
        // 初始化Loader,第一个参数是自定义的Loader ID,随便设个整数就行
        getLoaderManager().initLoader(1001, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // 定义要查询的联系人字段(只查需要的字段,提升性能)
        String[] projection = {
                ContactsContract.Contacts._ID,
                ContactsContract.Contacts.DISPLAY_NAME
        };
        // 返回CursorLoader实例,指定查询的URI、字段、条件等
        return new CursorLoader(
                this,
                ContactsContract.Contacts.CONTENT_URI,
                projection,
                null,
                null,
                null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // 查询完成,在这里处理返回的Cursor数据
        if (data != null && data.moveToFirst()) {
            do {
                String contactId = data.getString(data.getColumnIndex(ContactsContract.Contacts._ID));
                String contactName = data.getString(data.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));

                // 同样查询电话号码
                Cursor phoneCursor = getContentResolver().query(
                        ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                        null,
                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
                        new String[]{contactId},
                        null);

                if (phoneCursor != null) {
                    while (phoneCursor.moveToNext()) {
                        String phone = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                        Log.d("LoaderContact", "姓名:" + contactName + ",电话:" + phone);
                    }
                    phoneCursor.close();
                }
            } while (data.moveToNext());
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        // 当Loader重置时,清空相关数据(比如如果用Adapter的话,这里可以把Adapter的Cursor设为null)
    }
}

几个关键注意点

  • 不管用哪种方法,用完Cursor一定要调用close(),否则会造成内存泄漏。
  • 同步查询绝对不能放在UI线程,必须放在后台线程执行。
  • 一个联系人可能有多个电话号码,所以要遍历PhoneCursor获取所有号码。
  • 如果你的应用后续需要兼容API23+,可以在代码里加上权限检查的逻辑,但针对API17的话,只需要Manifest权限就够了。

内容的提问来源于stack exchange,提问作者codebeast

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:02:28