首先在向Android联系人中添加数据
联系人的数据放在
将contacts2.db导出到桌面上,打开
contact2.db有很多表,最重要的有3张表raw_contacts(存放联系人ID)、data、mimetypes
raw_contacts:
data:
mimetypes:
新建一个名为contacts 的项目,添加测试环境,新建一个测试类ContactsTest,放在cn.leigo.test包下。
现在我们看看源码是如何定义联系人的ContentProvider的,https://android.googlesource.com/platform/packages/providers/ContactsProvider.git/+/android-2.3.3_r1/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.providers.contacts" android:sharedUserId="android.uid.shared"> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.READ_SYNC_STATS" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.USE_CREDENTIALS" /> <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" /> <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.cp" /> <uses-permission android:name="android.permission.SUBSCRIBED_FEEDS_READ" /> <uses-permission android:name="android.permission.SUBSCRIBED_FEEDS_WRITE" /> <application android:process="android.process.acore" android:label="@string/app_label" android:icon="@drawable/app_icon"> <provider android:name="ContactsProvider2" android:authorities="contacts;com.android.contacts" android:label="@string/provider_label" android:multiprocess="false" android:readPermission="android.permission.READ_CONTACTS" android:writePermission="android.permission.WRITE_CONTACTS"> <path-permission android:pathPrefix="/search_suggest_query" android:readPermission="android.permission.GLOBAL_SEARCH" /> <path-permission android:pathPrefix="/search_suggest_shortcut" android:readPermission="android.permission.GLOBAL_SEARCH" /> <path-permission android:pathPattern="/contacts/.*/photo" android:readPermission="android.permission.GLOBAL_SEARCH" /> <grant-uri-permission android:pathPattern=".*" /> </provider> <provider android:name="CallLogProvider" android:authorities="call_log" android:syncable="false" android:multiprocess="false" android:readPermission="android.permission.READ_CONTACTS" android:writePermission="android.permission.WRITE_CONTACTS"> </provider> <!-- TODO: create permissions for social data --> <provider android:name="SocialProvider" android:authorities="com.android.social" android:syncable="false" android:multiprocess="false" android:readPermission="android.permission.READ_CONTACTS" android:writePermission="android.permission.WRITE_CONTACTS" /> <!-- Handles database upgrades after OTAs, then disables itself --> <receiver android:name="ContactsUpgradeReceiver"> <!-- This broadcast is sent after the core system has finished booting, before the home app is launched or BOOT_COMPLETED is sent. --> <intent-filter> <action android:name="android.intent.action.PRE_BOOT_COMPLETED"/> </intent-filter> </receiver> </application> </manifest>
这个类有6000多行,我们不可能全部看完,可以根据
sUriMatcher
查找或者直接找静态代码块
static { // Contacts URI matching table final UriMatcher matcher = sUriMatcher; matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions", AGGREGATION_SUGGESTIONS); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*", AGGREGATION_SUGGESTIONS); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_PHOTO); matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP); matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID); matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD); matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*", CONTACTS_AS_MULTI_VCARD); matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT); matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*", CONTACTS_STREQUENT_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP); matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS); matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID); matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA); matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID); matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES); matcher.addURI(ContactsContract.AUTHORITY, "data", DATA); matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID); matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES); matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID); matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS); matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID); matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP); matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS); matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID); matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS); matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID); matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY); matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE); matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#", SYNCSTATE_ID); matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP); matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions", AGGREGATION_EXCEPTIONS); matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*", AGGREGATION_EXCEPTION_ID); matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS); matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES); matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID); matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGESTIONS); matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGESTIONS); matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", SEARCH_SHORTCUT); matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts", LIVE_FOLDERS_CONTACTS); matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*", LIVE_FOLDERS_CONTACTS_GROUP_NAME); matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones", LIVE_FOLDERS_CONTACTS_WITH_PHONES); matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites", LIVE_FOLDERS_CONTACTS_FAVORITES); matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS); }
matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
package cn.leigo.test; import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; import android.test.AndroidTestCase; import android.text.TextUtils; import android.util.Log; public class ContactsTest extends AndroidTestCase { private static final String TAG = "ContactsTest"; /** * 获取联系人 * * @throws Exception */ public void testContacts() throws Exception { Uri uri = Uri.parse("content://com.android.contacts/contacts"); ContentResolver resolver = getContext().getContentResolver(); Cursor c = resolver .query(uri, new String[] { "_id" }, null, null, null); while (c.moveToNext()) { int contactid = c.getInt(0); StringBuilder sb = new StringBuilder("contactid=") .append(contactid); uri = Uri.parse("content://com.android.contacts/contacts/" + contactid + "/data"); Cursor cursor = resolver.query(uri, new String[] { "mimetype", "data1", "data2" }, null, null, null); while (cursor.moveToNext()) { String data1 = cursor.getString(cursor.getColumnIndex("data1")); String mimetypeId = cursor.getString(cursor .getColumnIndex("mimetype")); if (TextUtils .equals(mimetypeId, "vnd.android.cursor.item/name")) { // 姓名 sb.append(",name=" + data1); } else if (TextUtils.equals(mimetypeId, "vnd.android.cursor.item/email_v2")) { // email sb.append(",email=" + data1); } else if (TextUtils.equals(mimetypeId, "vnd.android.cursor.item/phone_v2")) { // 电话 sb.append(",phone=" + data1); } } Log.i(TAG, sb.toString()); } } }注意的是,android内部提供了关联查询,所以查询的时候可以直接查询"mimetype",而不是先查询"mimetype_id"然后根据"mimetype_id"查询"mimetype"。
最后添加上权限
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" />
/** * 根据号码获取联系人的姓名 */ public void testDisplayNameByNumber() throws Exception { String number = "15100000000"; Uri uri = Uri .parse("content://com.android.contacts/data/phones/filter/" + number); ContentResolver resolver = getContext().getContentResolver(); Cursor c = resolver.query(uri, new String[] { "display_name" }, null, null, null); if (c.moveToFirst()) { String name = c.getString(c.getColumnIndex("display_name")); Log.i(TAG, name); } c.close(); }
/** * 向通讯录添加联系人 * * @throws Exception */ public void testAddContact() throws Exception { Uri uri = Uri.parse("content://com.android.contacts/raw_contacts"); ContentResolver resolver = getContext().getContentResolver(); ContentValues values = new ContentValues(); // content://com.android.contacts/raw_contacts/2 long contactid = ContentUris.parseId(resolver.insert(uri, values)); // 添加姓名 uri = Uri.parse("content://com.android.contacts/data"); values.put("raw_contact_id", contactid); values.put("mimetype", "vnd.android.cursor.item/name"); values.put("data2", "张三"); resolver.insert(uri, values); // 添加电话 values.clear(); values.put("raw_contact_id", contactid); values.put("mimetype", "vnd.android.cursor.item/phone_v2"); values.put("data2", "2"); values.put("data1", "18900000000"); resolver.insert(uri, values); // 添加Email values.clear(); values.put("raw_contact_id", contactid); values.put("mimetype", "vnd.android.cursor.item/email_v2"); values.put("data2", "2"); values.put("data1", "[email protected]"); resolver.insert(uri, values); }
这种方式虽然能够添加成功,但是有个问题就是若某个数据没插入成功,那么其他数据依然会插入进去,没涉及到事务的操作。
下面提供另一种插入方法:
public void testAddContact2() throws Exception { Uri uri = Uri.parse("content://com.android.contacts/raw_contacts"); ContentResolver resolver = getContext().getContentResolver(); ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); ContentProviderOperation op1 = ContentProviderOperation.newInsert(uri) .withValue("account_name", null).build(); operations.add(op1); uri = Uri.parse("content://com.android.contacts/data"); ContentProviderOperation op2 = ContentProviderOperation.newInsert(uri) .withValueBackReference("raw_contact_id", 0) .withValue("mimetype", "vnd.android.cursor.item/name") .withValue("data2", "李四").build(); operations.add(op2); ContentProviderOperation op3 = ContentProviderOperation.newInsert(uri) .withValueBackReference("raw_contact_id", 0) .withValue("mimetype", "vnd.android.cursor.item/phone_v2") .withValue("data2", "2").withValue("data1", "13500000000") .build(); operations.add(op3); ContentProviderOperation op4 = ContentProviderOperation.newInsert(uri) .withValueBackReference("raw_contact_id", 0) .withValue("mimetype", "vnd.android.cursor.item/email_v2") .withValue("data2", "2").withValue("data1", "[email protected]") .build(); operations.add(op4); resolver.applyBatch("com.android.contacts", operations); }