1. ContactsProvider2.java (實現了ContentProvider)html
2. ContactsContract.java 全部的聯繫人Uri,與聯繫人相關數據庫字段均在此定義。這裏麪包含如下要講的contacts表,raw_contacts表,data表,phone_lookup表的字段及Uri描述。java
Table contactsandroid
_idsql |
starred數據庫 |
lookupwindows |
photo_id網絡 |
|
|
|
|
1app |
0eclipse |
省略ide |
<null> |
|
|
|
|
2 |
0 |
省略 |
<null> |
|
|
|
|
3 |
1 |
省略 |
2366 |
|
|
|
|
starred:標識該帳戶是否爲VIP帳戶
lookup: 沒具體看,也是一個很是重要的字段
photo_id: 引用data表的 _id,
Table raw_contacts
_id |
contact_id |
account_name |
account_type |
sync1 |
|
|
|
1 |
1 |
Yulei0619@gmail.com |
com.google |
http://www.google.com/m8/feeds/contacts/yulei0619@gmail.com/base2_property-android/1ac2a39e89c441cc |
|
|
|
2 |
2 |
Yulei0619@gmail.com |
com.google |
省略 |
|
|
|
3 |
3 |
pcsc |
com.htc.android.pcsc |
省略 |
|
|
|
contact_id:引用contacts表的_id字段
account_name:指明該聯繫人是從哪一個帳戶上同步下載下來的。
account_type:郵件賬戶的類型
sync1:說明該聯繫人信息是與哪一個網址進行交互(同步聯繫人信息)
Table data.
_id |
mimetype_id |
data1 |
data2 |
data15 |
|
|
1 |
1 |
6 |
南京用友 |
南京用友 |
<null> |
|
2 |
1 |
5 |
1-318-296-4252 |
1 |
<null> |
|
3 |
2 |
6 |
司道鵬 |
司道鵬 |
<null> |
|
4 |
2 |
5 |
1-516-920-8116 |
2 |
<null> |
|
5 |
3 |
6 |
子江 |
子江 |
<null> |
|
6 |
3 |
5 |
1795113734017562 |
1 |
<null> |
|
7 |
3 |
5 |
+8613734017562 |
2 |
<null> |
|
2366 |
3 |
4 |
<null> |
<null> |
‰PNG |
|
raw_contact_id: 引用 raw_contacts表的_id字段
mimetype_id:
vnd.android.cursor.item/photo -> 4 該行 data15表明照片
vnd.android.cursor.item/phone_v2 -> 5 該行data1表明電話號碼
vnd.android.cursor.item/name -> 6說明該行data1表明聯繫人名稱
data15: 存儲照片 (Type: Blob)
Table phone_lookup
_id |
data_id |
raw_contact_id |
normalized_number |
1 |
2 |
1 |
25246928131 |
2 |
4 |
2 |
61180296151 |
3 |
6 |
3 |
2657104373115971 |
4 |
7 |
3 |
2657104373168+ |
data_id: 引用 data 表的_id字段
raw_contact_id: 引用raw_contacts表的 _id字段
normalized_number:聯繫人電話號碼的倒序排列
如何將smsmms.db數據庫與contacts2.db數據結合起來成爲了很是關鍵的部分
依據聯繫人的number,查詢該聯繫人的contact_id
public static String getContactId(Context context, String number) { Cursor c = null; try { c = context.getContentResolver().query(Phone.CONTENT_URI, new String[] { Phone.CONTACT_ID, Phone.NUMBER }, null,, null); if (c != null && c.moveToFirst()) { while (!c.isAfterLast()) { if (PhoneNumberUtils.compare(number, c.getString(1))) { return c.getString(0); } c.moveToNext(); } } } catch (Exception e) { Log.e(TAG, "getContactId error:", e); } finally { if (c != null) {c.close(); } } return null; }
Phone.CONTENT_URI, = 「content:// com.android.contacts/data/phones」
經過查看源代碼發現:該URI主要對應着contacts表,raw_contacts表,data表。這段源碼對於剛瞭解該contact2數據庫的人說比較費勁,
「qb.setProjectionMap(distinct ? sDistinctDataProjectionMap : sDataProjectionMap);」是很是重要的線索,它告訴咱們會查詢哪些字段
在這裏尤爲須要說明的是:PhoneNumberUtils.compare(String a, String b) 方法
加入咱們在手機中把「13466739143」存儲爲聯繫人「張三」(存儲的聯繫手機號僅僅是13466739143),若是張三使用飛信給你發短信(咱們知道用飛信,電話號碼前會自動加12593),用戶確定但願在手機上的未接短信來自張三,而不是有「1259313466739143」被看成一個陌生號碼出現
如何將「1259313466739143」和「13466739143」以及可能的「1795113466739143」,「+8613466739143」等等認爲是同一個電話號碼,這裏的PhoneNumberUtils.compare(String a, String b)就起到了很是關鍵的做用。
有了contact_id以後,咱們就能夠作不少事情。
1.依據contact_id,去查詢該聯繫人的照片:
public static Bitmap getContactsPhoto(Context context, String contactId) { Cursor c = null; // load icon byte[] icon = null; try { // get contact photo URI Uri refUri = Uri.withAppendedPath(Contacts.CONTENT_URI, contactId); refUri = Uri.withAppendedPath(refUri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY); // get cursor c = context.getContentResolver().query(refUri, new String[] { Photo.PHOTO }, null, null, null); if (c != null && c.moveToFirst()) { icon = c.getBlob(0); } } catch (Exception e) { e.printStackTrace(); } finally { if (c != null) { c.close(); } } if (icon != null) { try { return BitmapFactory.decodeByteArray(icon, 0, icon.length); } catch (OutOfMemoryError e) { e.printStackTrace(); System.gc(); return null; } } return null; }
2. 依據該聯繫人的contact_id, 去查詢該聯繫人的名字(好比「張三」)
private static String getDisplayName(Context context, String contacts_id) { Cursor c = null; try { c = context.getContentResolver().query(Contacts.CONTENT_URI, new String[] { Contacts.DISPLAY_NAME }, Contacts._ID + "=" + contacts_id, null, null); if (c != null && c.moveToFirst()) { return c.getString(0); } } catch (Exception e) { e.printStackTrace(); } finally { if (c != null) { c.close(); } } return null; }
3. 依據該聯繫人的contact_id ,查詢同一個contact_id有多少個電話號碼。
(有可能手機上存了張三2個號碼)
public static int getContactNumberCount(Context context, String contactId) { int count = 0; Cursor c = null; try { c = context.getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, new String[] { ContactsContract.CommonDataKinds.Phone.NUMBER}, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null); if (c != null && c.moveToFirst()) { count = c.getCount(); } } catch (Exception e) { e.printStackTrace(); } finally { if (c != null) { c.close(); } } return count; }
------------------------------------------------------------------------------
若是須要讀取一個聯繫人的信息用CONTENT_LOOKUP_RUI代替CONTENT_URI
若是須要經過電話號碼查找一個聯繫人,用PhoneLookup.CONTENT_FIILTER_URI,這個URI爲這個目的進行了優化;
若是須要經過部分名字的匹配查找,用CONTENT_FILTER_URI;
Android中的通訊錄操做給人的第一感受就是暈,不知道怎麼去用。能夠看下這張我畫的圖:(比較醜,可是地球人都能看懂)
經過查詢文檔: If you need to look up a contact by the phone number, usePhoneLookup.CONTENT_FILTER_URI, which is optimized for this purpose.
咱們肯定了URI以後就能夠開始編寫代碼了。
public String lookupNameByPhoneNumber(Context context,String phoneNumber) {
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor cursor = context.getContentResolver().query(uri,
new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);
String name = null;
if (cursor == null) {
name = null;
}
try {
if (cursor.moveToFirst()) {
name = cursor.getColumnName(0);
}
} finally {
cursor.close();
}
return name;
}
看完這段代碼,你可能想去問,第二步跟第三步跑哪裏去了。這兩步其實已經被封裝在Resoler的query方法中了。若是真要咱們編寫這兩部的話,那咱們還不如不用ContentProvider來的輕鬆。
最後再來講下真機上的通訊錄模型,真機上的通訊錄其實就是原生通訊錄的擴展。增長一些東西。
最重要的一點,原生數據庫裏有的表,表字段,觸發器,視圖或索引,在真機上確定也有。
若是想本身作一個通訊錄,也確定要在原生的通訊錄上擴展,只能增長,不能減小。
想問爲何?
若是你少兩張表的話,可能一些系統功能就崩潰了。
這就不符合google的目的了,我提供了一些功能,你可使用修改,可是你別刪除,因此google火了。
添加一條數據
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType()) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName()) .build()); ops.add(ContentProviderOperation .newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name) .build()); ops.add(ContentProviderOperation .newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType).build()); ops.add(ContentProviderOperation .newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Email.DATA, email) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType).build()); // Ask the Contact provider to create a new contact Log.i(TAG, "Selected account: " + mSelectedAccount.getName() + " (" + mSelectedAccount.getType() + ")"); Log.i(TAG, "Creating contact: " + name); try { getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); } catch (Exception e) { // Display warning }
---------------------------------------------------------
Android系統中的聯繫人也是經過ContentProvider來對外提供數據的,咱們這裏實現獲取全部聯繫人、經過電話號碼獲取聯繫人、添加聯繫人、使用事務添加聯繫人。
獲取全部聯繫人
1. Android系統中的聯繫人也是經過ContentProvider來對外提供數據的
2. 數據庫路徑爲:/data/data/com.android.providers.contacts/database/contacts2.db
3. 咱們須要關注的有3張表
raw_contacts:其中保存了聯繫人id
data:和raw_contacts是多對一的關係,保存了聯繫人的各項數據
mimetypes:爲數據類型
4. Provider的authorites爲com.android.contacts
5. 查詢raw_contacts表的路徑爲:contacts
6. 查詢data表的路徑爲:contacts/#/data
這個路徑爲鏈接查詢,要查詢「mimetype」字段能夠根據「mimetype_id」查詢到mimetypes表中的數據
7. 先查詢raw_contacts獲得每一個聯繫人的id,在使用id從data表中查詢對應數據,根據mimetype分類數據
示例:
經過電話號碼獲取聯繫人
1. 系統內部提供了根據電話號碼獲取data表數據的功能,路徑爲:data/phones/filter/*
2. 用電話號碼替換「*」部分就能夠查到所需數據,獲取「display_name」能夠獲取到聯繫人顯示名
示例:
添加聯繫人
1. 先向raw_contacts表插入id,路徑爲:raw_contacts
2. 獲得id以後再向data表插入數據,路徑爲:data
示例:
使用事務添加聯繫人
1. 在添加聯繫人得時候是分屢次訪問Provider,若是在過程當中出現異常,會出現數據不完整的狀況,這些操做應該放在一次事務中
2. 使用ContentResolver的applyBatch(String authority,ArrayList<ContentProviderOperation> operations) 方法能夠將多個操做在一個事務中執行
3. 文檔位置:
file:///F:/android-sdk-windows/docs/reference/android/provider/ContactsContract.RawContacts.html
示例:
//使用事務添加聯繫人 public void testInsertBatch() throws Exception { ContentResolver resolver = getContext().getContentResolver(); ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); ContentProviderOperation operation1 = ContentProviderOperation // .newInsert(Uri.parse("content://com.android.contacts/raw_contacts")) // .withValue("_id", null) // .build(); operations.add(operation1); ContentProviderOperation operation2 = ContentProviderOperation // .newInsert(Uri.parse("content://com.android.contacts/data")) // .withValueBackReference("raw_contact_id", 0) // .withValue("data2", "ZZH") // .withValue("mimetype", "vnd.android.cursor.item/name") // .build(); operations.add(operation2); ContentProviderOperation operation3 = ContentProviderOperation // .newInsert(Uri.parse("content://com.android.contacts/data")) // .withValueBackReference("raw_contact_id", 0) // .withValue("data1", "18612312312") // .withValue("data2", "2") // .withValue("mimetype", "vnd.android.cursor.item/phone_v2") // .build(); operations.add(operation3); ContentProviderOperation operation4 = ContentProviderOperation // .newInsert(Uri.parse("content://com.android.contacts/data")) // .withValueBackReference("raw_contact_id", 0) // .withValue("data1", "zq@itcast.cn") // .withValue("data2", "2") // .withValue("mimetype", "vnd.android.cursor.item/email_v2") // .build(); operations.add(operation4); // 在事務中對多個操做批量執行 resolver.applyBatch("com.android.contacts", operations); }