Giới thiệu
Hướng dẫn truy xuất danh sách toàn bộ liên hệ từ ContactsContract ContentProvider, bao gồm tên và số điện thoại, với xử lý quyền runtime (Android 6.0+).
Các bước thực hiện
Bước 1: Tạo dự án & Giao diện
Layout: ListView hiển thị tên liên hệ.
Bước 2: AndroidManifest.xml - Khai báo quyền
<uses-permission android:name="android.permission.READ_CONTACTS" />
Bước 3: MainActivity.java
package com.example.myapplication;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_READ_CONTACTS = 79;
ListView listView;
ArrayList<String> contactNames;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.list);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
== PackageManager.PERMISSION_GRANTED) {
loadContacts();
} else {
requestPermission();
}
}
private void loadContacts() {
contactNames = getAllContactNames();
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1, contactNames);
listView.setAdapter(adapter);
}
private void requestPermission() {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_READ_CONTACTS);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_READ_CONTACTS) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
loadContacts();
} else {
// Xử lý từ chối quyền
}
}
}
private ArrayList<String> getAllContactNames() {
ArrayList<String> names = new ArrayList<>();
ContentResolver cr = getContentResolver();
// Chỉ lấy các cột cần thiết để tối ưu hiệu suất
String[] projection = {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.HAS_PHONE_NUMBER
};
Cursor cursor = cr.query(
ContactsContract.Contacts.CONTENT_URI,
projection, null, null,
ContactsContract.Contacts.DISPLAY_NAME + " ASC");
if (cursor != null && cursor.getCount() > 0) {
while (cursor.moveToNext()) {
String name = cursor.getString(
cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
names.add(name);
}
cursor.close();
}
return names;
}
}
Mở rộng: Lấy cả số điện thoại
Nếu cần số điện thoại, truy vấn thêm bảng ContactsContract.CommonDataKinds.Phone:
private void loadContactsWithPhones() {
ContentResolver cr = getContentResolver();
String[] projection = {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME
};
Cursor contactCursor = cr.query(ContactsContract.Contacts.CONTENT_URI, projection, null, null, null);
if (contactCursor != null) {
while (contactCursor.moveToNext()) {
String id = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID));
String name = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
// Truy vấn số điện thoại của contact này
Cursor phoneCursor = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER},
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{id}, null);
if (phoneCursor != null) {
while (phoneCursor.moveToNext()) {
String phone = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
// Lưu cặp name-phone vào model/list
}
phoneCursor.close();
}
}
contactCursor.close();
}
}
Tối ưu hiệu suất (Best Practices)
- Chỉ query các cột cần thiết (
projection). - Dùng
CursorLoader(deprecated) hoặc ContentResolver.query() trên background thread (RxJava, Coroutines, Executor). - Phân trang (pagination) nếu danh sách lớn.
- Cache kết quả nếu không thay đổi thường xuyên.
Chạy ứng dụng
Nhấn Run. Cấp quyền READ_CONTACTS → ListView hiển thị tất cả tên liên hệ.

Lưu ý phiên bản Android
- Android 6.0+ (API 23): Bắt buộc xin quyền runtime.
- Android 10+ (API 29): Cần quyền
READ_PRIVILEGED_PHONE_STATEcho một số trường nhạy cảm. - Android 11+ (API 30): Package visibility - thêm
<queries>trong Manifest nếu dùng Intent.