现在的位置: 首页 > 综合 > 正文

Android开发入门之访问通讯录中的联系人和添加联系人

2016年08月21日 ⁄ 综合 ⁄ 共 11733字 ⁄ 字号 评论关闭

首先在向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>

来到https://android.googlesource.com/platform/packages/providers/ContactsProvider.git/+/android-2.3.3_r1/src/com/android/providers/contacts/ContactsProvider2.java,

这个类有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);
    }

目前为止,我们只关心3个路径

        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", "zhangsan@csdn.net");
		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", "lisi@csdn.net")
				.build();
		operations.add(op4);

		resolver.applyBatch("com.android.contacts", operations);
	}

抱歉!评论已关闭.