如何在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)配合使用。步骤如下:
- 让你的Activity/Fragment实现
LoaderManager.LoaderCallbacks<Cursor>接口 - 在onCreate里初始化Loader
- 实现三个回调方法处理查询逻辑
示例代码:
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
相关产品推荐
相关产品推荐

