1. 程式人生 > >Android-讀取/新增/作業系統聯絡人資料

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: 聯絡人:

 

效果圖: