Android-读取/新增/操作系统联系人数据

想要访问Android操作系统的ContentProvider就需要明白以下原理:

在Android操作系统里面的 /packsges/目录:

  apps: 很多的系统应用,例如:联系人,浏览器,音乐播放器,设置,相机 ......

                                      ............

  providers:系统对外暴露的ContentProvider:

                      

  这样就明白了,Android操作系统里面的应用(apps),是访问系统对外暴露的ContentProvider(providers)


先在模拟器,添加联系人:

既然说Android操作系统里面的应用(apps),是访问系统对外暴露的ContentProvider(providers):

  /data/data/com.android.contacts(放置在apps目录里面)

  /data/data/com.android.providers.contacts(放置在providers目录里面)

contacts(联系人应用) 是调用 providers.contacts(内容提供者里面的数据),如图:

(为什么apps目录的contacts应用 能够访问provider目录的provider.contacts应用?,因为provider目录的provider.contacts应用通过ContentProvider对外暴露)


阅读 com.android.providers.contacts 数据库与表

Android操作系统的文件系统目录/data/data/com.android.contacts,是访问/data/data/com.android.providers.contacts(内容提供者应用)

所以需要阅读/data/data/com.android.providers.contacts(内容提供者应用)的数据库:

打开 contacts2.db

 打开后:表非常多,视图非常多,等等, 但是只需要关注三张核心的表

                         1.raw_contacts(联系人唯一标识表:里面有 _id 的字段,是标识了一个联系人的唯一标识)

                         2.data(通过 raw_contacts 表里面的_id,查询 raw_contacts_id,就能得到查询结果)

                                           得到查询结果后:data1字段: 联系人数据,data2字段:数据对应的分类(家庭,住宅....)

                                           得到查询结果后:mimetype_id字段,是用来查询的mimetypes表,数据对应的(姓名,..)

                         3.mimetypes(通过 data 表里面的 mimetype_id,查询对应的值)

raw_contacts表:

data表:

mimetypes表:


阅读 com.android.providers.contacts AndroidManifest.xml,和ContactsProvider2.java

apps目录的contacts应用,是访问provider目录的provider.contacts应用,所以要阅读Android操作系统源码-->packages/providers/ContactsProvider(内容提供者)

AndroidManifest.xml

1.ContactsProvider2就是联系人的内容提供者

2.android:authorities="contacts;com.android.contacts" Android2.2版本之前的授权contacts,后面为了考虑兼容性就 ;com.android.contacts,这样就两个授权都可以使用了,相当于Web服务器项目的两个域名,都可以访问服务器

3.android:exported="true" 允许对外输出

4.设定被访问的权限 android:readPermission="android.permission.READ_CONTACTS" 联系人内容提供者读取权限

           android:writePermission="android.permission.WRITE_CONTACTS" 联系人内容提供者修改权限

        <provider android:name="ContactsProvider2"
            android:authorities="contacts;com.android.contacts"
            android:label="@string/provider_label"
            android:multiprocess="false"
            android:exported="true"
            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>

ContactsProvider2.java

首先要找到的就是Uri,所以搜索UriMatcher的matcher.addURI

    matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
                CONTACTS_LOOKUP_DISPLAY_PHOTO);
        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
                CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
                CONTACTS_LOOKUP_ENTITIES);
        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
                CONTACTS_LOOKUP_ID_ENTITIES);
        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
                CONTACTS_LOOKUP_STREAM_ITEMS);
        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
        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, "contacts/frequent", CONTACTS_FREQUENT);
        matcher.addURI(ContactsContract.AUTHORITY, "contacts/delete_usage", CONTACTS_DELETE_USAGE);

        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_ID_DATA);

有规律,通常情况下,matcher.addURI(授权, path, code),第二个参数 path 和数据库表名对应的

我首先要查询,raw_contacts 表,所以找到查询这个表的 Uri :matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);

然后还要查询,data 表,所要要查询这个表的 Uri :

      注意用这个关联查询: matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_ID_DATA);

       这个不是关联查询: matcher.addURI(ContactsContract.AUTHORITY, "data", DATA); 

 

写入数据需要, 往raw_contacts表增加一条数据,作为唯一标示 matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);

写入数据需要,不需要关联查询的Uir,需要写入到Data表:matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);


读取 -->> Android操作系统联系人内容提供者应用里面的联系人数据:

增加数据到  Android操作系统联系人内容提供者应用里面

 

package liudeli.cp.client;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class ContactsActivity extends Activity {

    private final String TAG  = ContactsActivity.class.getSimpleName();

    private EditText name;
    private EditText phone;
    private EditText email;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contacts);

        name = findViewById(R.id.et_name);
        phone = findViewById(R.id.et_phone);
        email = findViewById(R.id.et_email);
    }

    /**
     * 获取 /data/data/com.android.providers.contacts应用(内容提供者暴露的联系人数据)
     *     packages/providers/com.android.providers.contacts应用(内容提供者暴露的联系人数据)
     * @param view
     */
    public void readContacts(View view) {
        /**
         *  复制com.android.providers.contacts应用的Uri
         *  matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
         *  matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_ID_DATA);
         *
         *  授权:android:authorities="contacts;com.android.contacts"
         */
        Uri rawContactsURI = Uri.parse("content://com.android.contacts/raw_contacts");
        Uri rawContactsByIdDataURI = Uri.parse("content://com.android.contacts/raw_contacts/#/data");

        // 拿到内容解决者(ContentResolver)
        ContentResolver contentResolver = getContentResolver();

        /**
         * 使用内容解决者 读取 操作系统联系人内容提供者应用raw_contacts表
         */
        Cursor cursor = contentResolver.query(rawContactsURI,
                              new String[]{"_id"},
                            null,
                        null ,
                           null);
        while (cursor.moveToNext()) {
            // cursor.getInt(0);
            int _id = cursor.getInt(cursor.getColumnIndex("_id"));
            Log.d(TAG, "_id" + _id);

            /**
             * 使用内容解决者 读取 操作系统联系人内容提供者应用data表 (根据raw_contacts表的_id 去查询)
             */
            // 把raw_contacts表的_id 写到 rawContactsByIdDataURI2里面去
            // 把_id写到...raw_contacts/#/data 的#
            // 第一种方式 结果为:content://com.android.contacts/raw_contacts/4#/data
            // 考虑到 操作系统的联系人内容提供者不是ContentUris.parseId(uri),所以用第二种拼接方式
            // Uri rawContactsByIdDataURI2 = ContentUris.withAppendedId(rawContactsByIdDataURI, _id);

            // 第二种方式 是直接拼接
            Uri rawContactsByIdDataURI2 = Uri.parse("content://com.android.contacts/raw_contacts/"+_id+"/data");
            // Log.d(TAG, "rawContactsByIdDataURI2:" + rawContactsByIdDataURI2.toString());
            // 结果 content://com.android.contacts/raw_contacts/4/data

            Cursor dataCursor = contentResolver.query(rawContactsByIdDataURI2,
                                    new String[]{},
                                    null,
                                    null,
                                    null,
                                null);
            /**
             * 注意:⚠️ 由于content://com.android.contacts/raw_contacts/#/data这是表关联查询
             *         所以查询出来的字段,我不知道,这系统去关联查询的,所以打印出来看看
             */
            /*String[] columnNames = dataCursor.getColumnNames();
            for (String columnName : columnNames) {
                Log.d(TAG, "关联查询Data表 的所有字段" + columnName);
            }*/
            /**
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段sort_key
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段photo_uri
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段status_label
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段status_ts
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段status_res_package
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段name_verified
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段display_name
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段last_time_used
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段mimetype
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段phonebook_label_alt
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data6
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段version
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段photo_id
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data3
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段custom_ringtone
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段times_contacted
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段account_type_and_data_set
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段dirty
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data7
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data15
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段raw_contact_is_user_profile
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data_set
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段phonebook_label
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data10
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段res_package
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段account_type
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data11
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段display_name_alt
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段lookup
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段phonetic_name
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段last_time_contacted
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段contact_last_updated_timestamp
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data13
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段in_visible_group
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段chat_capability
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data9
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data_sync1
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段sort_key_alt
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段contact_presence
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data_version
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段phonetic_name_style
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段name_raw_contact_id
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段raw_contact_id
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段send_to_voicemail
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data4
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data12
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段contact_status
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段contact_status_label
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段pinned
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段status_icon
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段status
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data1
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段phonebook_bucket
             * 12-15 22:45:46.355 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data_sync2
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段contact_status_res_package
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段in_default_directory
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段_id
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段is_super_primary
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data5
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段contact_id
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data8
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段is_primary
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data_sync4
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段has_phone_number
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段display_name_source
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段photo_file_id
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data_sync3
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data14
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段contact_status_ts
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段phonebook_bucket_alt
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段mode
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段data2
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段group_sourceid
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段starred
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段photo_thumb_uri
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段times_used
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段contact_status_icon
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段contact_chat_capability
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段account_name
             * 12-15 22:45:46.356 4432-4432/liudeli.cp.client D/ContactsActivity: 关联查询Data表 的所有字段sourceid
             */

            Cursor dataCursor2 = contentResolver.query(rawContactsByIdDataURI2,
                    new String[]{"data1", "data2", "mimetype"},
                    null,
                    null,
                    null,
                    null);
            while (dataCursor2.moveToNext()) {
                String data1 = dataCursor2.getString(0);
                String data2 = dataCursor2.getString(1);
                String mimetype = dataCursor2.getString(2);
                // Log.d(TAG, "data1" + data1 + " ,data2:" + data2 + " ,mimetype:" + mimetype);

                StringBuilder sb = new StringBuilder();

                /**
                 * mimetypes表:
                 * vnd.android.cursor.item/name
                 * vnd.android.cursor.item/phone_v2
                 * vnd.android.cursor.item/email_v2
                 * ...
                 */
                if ("vnd.android.cursor.item/name".equals(mimetype)) {
                    // 是姓名
                    if ("1".equals(data2)) {
                        // data2 == 1  是家庭相关
                        // Log.i(TAG, "家庭姓名:" + data1);
                        sb.append("家庭姓名:" + data1 + ",  ");
                    } else {
                        // Log.i(TAG, "姓名:" + data1);
                        sb.append("姓名:" + data1 + ",  ");
                    }
                } else if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) {
                    // 是手机号码
                    if ("1".equals(data2)) {
                        // data2 == 1  是家庭相关
                        // Log.i(TAG, "家庭号码:" + data1);
                        sb.append("家庭号码:" + data1 + ",  ");
                    } else {
                        // Log.i(TAG, "号码:" + data1);
                        sb.append("号码:" + data1 + ",  ");
                    }
                } else if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
                    // 是邮箱
                    if ("1".equals(data2))  {
                        // data2 == 1  是家庭相关
                        // Log.i(TAG, "家庭邮箱" + data1);
                        sb.append("家庭邮箱" + data1 + ",  ");
                    } else {
                        // Log.i(TAG, "邮箱" + data1);
                        sb.append("邮箱" + data1 + ",  ");
                    }
                }

                Log.i(TAG, "联系人:" + sb.toString());

            }
        }
    }


    /**
     * 写入 /data/data/com.android.providers.contacts应用(内容提供者暴露的联系人数据)
     *     packages/providers/com.android.providers.contacts应用(内容提供者暴露的联系人数据)
     * @param view
     */
    public void insertContacts(View view) {

        /**
         *  复制com.android.providers.contacts应用的Uri
         *  matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
         *  matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
         *
         *  授权:android:authorities="contacts;com.android.contacts"
         */
        Uri rawContactsURI = Uri.parse("content://com.android.contacts/raw_contacts");
        Uri dataUri = Uri.parse("content://com.android.contacts/data");

        if (!TextUtils.isEmpty(name.getText()) && !TextUtils.isEmpty(phone.getText()) && !TextUtils.isEmpty(email.getText())) {
            /**
             * 思路:先往raw_contacts表增加一条记录,返回Uri,通过返回的Uri,获取id
             *      通过获取的id,在去增加data表,把id插入到data表的raw_contacts_id字段中去(建立关联关系)
             */

            // 先往raw_contacts表增加一条记录,返回Uri,通过返回的Uri,获取id
            // 拿到内容解决者(ContentResolver)
            ContentResolver contentResolver = getContentResolver();

            /**
             * 使用内容解决者 写入到 操作系统联系人内容提供者应用raw_contacts表
             */
            ContentValues contentValues = new ContentValues();
            Uri rawContactsTaleInsertResult = contentResolver.insert(rawContactsURI, contentValues);

            // 通过返回的Uri,解析id,获取id
            long insertResultID = ContentUris.parseId(rawContactsTaleInsertResult);

            contentValues.clear();

            // 通过获取的id,在去增加data表,把id插入到data表的raw_contacts_id字段中去(建立关联关系)
            /**
             * mimetypes表:
             * vnd.android.cursor.item/name
             * vnd.android.cursor.item/phone_v2
             * vnd.android.cursor.item/email_v2
             * ...
             */
            contentValues.put("raw_contact_id", insertResultID);
            contentValues.put("mimetype", "vnd.android.cursor.item/name"); // 类型区分(例如:姓名,电话,邮箱...)
            contentValues.put("data1", name.getText().toString());
            contentResolver.insert(dataUri, contentValues);

            contentValues.put("raw_contact_id", insertResultID);
            contentValues.put("mimetype", "vnd.android.cursor.item/phone_v2"); // 类型区分(例如:姓名,电话,邮箱...)
            contentValues.put("data1", phone.getText().toString());
            contentValues.put("data2", 1); // 类型区分(例如:家用,公司,....)
            contentResolver.insert(dataUri, contentValues);

            contentValues.put("raw_contact_id", insertResultID);
            contentValues.put("mimetype", "vnd.android.cursor.item/phone_v2"); // 类型区分(例如:姓名,电话,邮箱...)
            contentValues.put("data1", email.getText().toString());
            contentValues.put("data2", 1); // 类型区分(例如:家用,公司,....)
            Uri insertExit = contentResolver.insert(dataUri, contentValues);

            Toast.makeText(this, "插入联系人完成", Toast.LENGTH_LONG).show();

        } else {
            Toast.makeText(this, "请填写完整信息", Toast.LENGTH_LONG).show();
        }
    }
}

Layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="读取联系人"
        android:onClick="readContacts"
        />

    <Button
        android:id="@+id/bt_insert"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:text="写入联系人"
        android:onClick="insertContacts"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_below="@id/bt_insert"
        android:layout_margin="20dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <!-- 我先放置控件 -->
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="请输入姓名:"
                />

            <!-- 剩下的空间给我来全部全部填充 -->
            <EditText
                android:id="@+id/et_name"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <!-- 我先放置控件 -->
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="请输入号码:"
                />

            <!-- 剩下的空间给我来全部全部填充 -->
            <EditText
                android:id="@+id/et_phone"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <!-- 我先放置控件 -->
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="请输入邮箱:"
                />

            <!-- 剩下的空间给我来全部全部填充 -->
            <EditText
                android:id="@+id/et_email"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                />

        </LinearLayout>

    </LinearLayout>

</RelativeLayout>

AndroidManifest.xml 权限:

<!--
        访问操作系统联系人内容提供者应用,需要加入的权限
        android:readPermission="android.permission.READ_CONTACTS"
        android:writePermission="android.permission.WRITE_CONTACTS"
    -->
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />

读取的Log日志信息,在添加联系人的时候给雷军增加了很多号码,所以日志就多一些:

12-15 23:37:32.731 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:号码:1 368-787-8767, 
12-15 23:37:32.731 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:姓名:马化腾, 
12-15 23:37:32.731 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:
12-15 23:37:32.731 5365-5365/liudeli.cp.client D/ContactsActivity: _id2
12-15 23:37:32.786 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:号码:1 667-878-9877, 
12-15 23:37:32.786 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:家庭号码:536-5465, 
12-15 23:37:32.786 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:姓名:马云, 
12-15 23:37:32.786 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:
12-15 23:37:32.786 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:
12-15 23:37:32.786 5365-5365/liudeli.cp.client D/ContactsActivity: _id3
12-15 23:37:32.793 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:号码:1 378-888-8989, 
12-15 23:37:32.793 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:姓名:黄光裕, 
12-15 23:37:32.793 5365-5365/liudeli.cp.client D/ContactsActivity: _id4
12-15 23:37:32.799 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:号码:1 869-999-9999, 
12-15 23:37:32.799 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:家庭号码:(646) 456-56, 
12-15 23:37:32.799 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:号码:(345) 345-464, 
12-15 23:37:32.799 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:号码:(654) 745-7657, 
12-15 23:37:32.799 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:号码:575-756, 
12-15 23:37:32.799 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:姓名:雷军, 
12-15 23:37:32.799 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:
12-15 23:37:32.799 5365-5365/liudeli.cp.client I/ContactsActivity: 联系人:

效果图:

猜你喜欢

转载自www.cnblogs.com/android-deli/p/10124573.html