原代碼查詢 ContactsContract.Contacts.CONTENT_URI 獲取聯系人 ID,再用 Phone._ID 作為查詢條件去查 Phone.CONTENT_URI,導致 phones.getCount() == 0。**錯誤點:Phone 表的外鍵是 CONTACT_ID,而非 _ID。**
_ID (即 CONTACT_ID)。Phone.CONTACT_ID = ? 查詢 Phone.CONTENT_URI。Phone.NUMBER 和 Phone.TYPE。public void loadContactsWithPhones() {
ContentResolver cr = getContentResolver();
// 1. 查詢聯系人基本信息
String[] projection = new String[]{
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.HAS_PHONE_NUMBER
};
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
do {
String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String hasPhone = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
if ("1".equals(hasPhone)) { // 有電話號碼
// 2. 查詢該聯系人的所有電話
Cursor phones = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{contactId},
null
);
if (phones != null) {
while (phones.moveToNext()) {
String number = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
int type = phones.getInt(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));
String typeLabel = getPhoneTypeLabel(type);
Log.d("Contact", name + " - " + typeLabel + ": " + number);
}
phones.close();
}
}
} while (cursor.moveToNext());
cursor.close();
}
}
private String getPhoneTypeLabel(int type) {
switch (type) {
case ContactsContract.CommonDataKinds.Phone.TYPE_HOME: return "住宅";
case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE: return "手機";
case ContactsContract.CommonDataKinds.Phone.TYPE_WORK: return "工作";
default: return "其他";
}
}
| 錯誤寫法 | 正確寫法 | 說明 |
|---|---|---|
Phone._ID + " = " + contactId | Phone.CONTACT_ID + " = ?" | Phone 表關聯聯系人表的外鍵是 CONTACT_ID |
| 字符串拼接 SQL | 參數化查詢 new String[]{contactId} | 防止 SQL 注入,處理特殊字符 |
| 未檢查 hasPhone | 先判斷 HAS_PHONE_NUMBER == "1" | 避免無號碼聯系人無效查詢 |
| 未關閉 Cursor | 嚴格在 finally 或 use-after-close 關閉 | 防止內存洩漏,Cursor 是系統資源 |
<uses-permission android:name="android.permission.READ_CONTACTS" />
Android 6.0+ 還需運行時請求權限。
CursorLoader (LoaderManager) 異步加載,避免阻塞主線程。projection),不要用 null 查所有列。掌握 ContactsContract 正確關聯查詢邏輯,是 Android 聯系人開發的基礎功課。