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

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

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

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

  providers:系统对外暴露的ContentProvider:

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

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


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

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

既然说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对外暴露)

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


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

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

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

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

打开 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表:

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

data表:

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

mimetypes表:

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


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

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

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

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: 联系人:

效果图:

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

05-20 19:02