一、利用ContentResolver读写联系人
1、因为一个联系人可能有多个电话号码,还可能有多个邮箱,所以系统通讯录将其设计为三张表,分别是联系人基本信息表、联系号码表、联系邮箱表,于是每添加一位联系人,就要调用至少三次insert方法
2、同理,联系人读取代码也分成三个步骤,先查出联系人的基本信息,再依次查询联系人号码和联系人邮箱
3、通讯录相关的表
raw_contacts表
data表
mimetypes表
(1)raw_contacts表的_id与data表的raw_contact_id对应。data表的mimetype_id与mimetypes表的_id对应
(2)data表记录了用户通讯录所有数据,包括手机号,显示名称等。数据类型是在mimetypes表中定义
4、例子
联系人实体类Contact.java
java
package com.example.chapter07_client.entity;
public class Contact {
private String name;
private String phone;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Contact{" +
"name='" + name + '\'' +
", phone='" + phone + '\'' +
", email='" + email + '\'' +
'}';
}
}
ContactAddActivity.java
java
package com.example.chapter07_client;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.provider.ContactsContract;
import com.example.chapter07_client.entity.Contact;
public class ContactAddActivity extends AppCompatActivity implements View.OnClickListener {
private EditText et_contact_name;
private EditText et_contact_phone;
private EditText et_contact_email;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contact_add);
et_contact_name = findViewById(R.id.et_contact_name);
et_contact_phone = findViewById(R.id.et_contact_phone);
et_contact_email = findViewById(R.id.et_contact_email);
findViewById(R.id.btn_contact_add).setOnClickListener(this);
findViewById(R.id.btn_contact_query).setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_contact_add) {
Contact contact = new Contact();
contact.setName(et_contact_name.getText().toString());
contact.setPhone(et_contact_phone.getText().toString());
contact.setEmail(et_contact_email.getText().toString());
// 方式一:使用ContentResolver多次写入,每次一个字段
addContacts(getContentResolver(), contact);
} else if (view.getId() == R.id.btn_contact_query) {
}
}
// 往手机通讯录添加一个联系人信息(包括姓名、电话号码、邮箱地址)
private void addContacts(ContentResolver resolver, Contact contact) {
// 插入一条空记录
ContentValues values = new ContentValues();
// 往raw_contacts表添加联系人记录,并获取添加后的联系人编号
Uri uri = resolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(uri);
// 插入姓名
ContentValues name = new ContentValues();
// 关联联系人编号
name.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
// "姓名"的数据类型
name.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
// 联系人姓名
name.put(ContactsContract.Contacts.Data.DATA2, contact.getName());
// 往data表插入记录
resolver.insert(ContactsContract.Data.CONTENT_URI, name);
// 插入电话号码
ContentValues phone = new ContentValues();
// 关联联系人编号
phone.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
// "电话号码"的数据类型
phone.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
// 联系人电话号码
phone.put(ContactsContract.Contacts.Data.DATA1, contact.getPhone());
// 电话号码类型,1表示家庭,2表示工作
phone.put(ContactsContract.Contacts.Data.DATA2, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
// 往data表插入记录
resolver.insert(ContactsContract.Data.CONTENT_URI, phone);
// 插入邮箱地址
ContentValues email = new ContentValues();
// 关联联系人编号
email.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
// "邮箱地址"的数据类型
email.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
// 联系人邮箱地址
email.put(ContactsContract.Contacts.Data.DATA1, contact.getEmail());
// 邮箱地址类型,1表示家庭,2表示工作
email.put(ContactsContract.Contacts.Data.DATA2, ContactsContract.CommonDataKinds.Email.TYPE_WORK);
// 往data表插入记录
resolver.insert(ContactsContract.Data.CONTENT_URI, email);
}
}
布局文件activity_contact_add.xml
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ContactAddActivity">
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2"
android:rowCount="4">
<TextView
android:layout_width="110dp"
android:layout_height="wrap_content"
android:text="联系人姓名:"
android:textSize="17sp"/>
<EditText
android:id="@+id/et_contact_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:inputType="text"
android:hint="请输入姓名"/>
<TextView
android:layout_width="110dp"
android:layout_height="wrap_content"
android:text="联系人号码:"
android:textSize="17sp"/>
<EditText
android:id="@+id/et_contact_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:inputType="phone"
android:hint="请输入号码"/>
<TextView
android:layout_width="110dp"
android:layout_height="wrap_content"
android:text="联系人邮箱:"
android:textSize="17sp"/>
<EditText
android:id="@+id/et_contact_email"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:inputType="textEmailAddress"
android:hint="请输入邮箱"/>
</GridLayout>
<Button
android:id="@+id/btn_contact_add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="添加联系人"
android:textSize="17sp"/>
<Button
android:id="@+id/btn_contact_query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="查询联系人"
android:textSize="17sp"/>
</LinearLayout>
5、添加成功

6、方式一,是分几句分别添加,可能会有部分成功、部分失败的情况
所以可以用方式二,类似于事务要么全部成功,要么全部失败
方式二,批处理方式
每一次操作都是一个ContentProviderOperation,构建一个操作集合,然后一次性执行
好处是,要么全部成功,要么全部失败,保证了事务的一致性
7、方式二批量添加,修改ContactAddActivity.java
java
package com.example.chapter07_client;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.view.View;
import android.widget.EditText;
import android.provider.ContactsContract;
import com.example.chapter07_client.entity.Contact;
import java.util.ArrayList;
public class ContactAddActivity extends AppCompatActivity implements View.OnClickListener {
private EditText et_contact_name;
private EditText et_contact_phone;
private EditText et_contact_email;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contact_add);
et_contact_name = findViewById(R.id.et_contact_name);
et_contact_phone = findViewById(R.id.et_contact_phone);
et_contact_email = findViewById(R.id.et_contact_email);
findViewById(R.id.btn_contact_add).setOnClickListener(this);
findViewById(R.id.btn_contact_query).setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_contact_add) {
Contact contact = new Contact();
contact.setName(et_contact_name.getText().toString());
contact.setPhone(et_contact_phone.getText().toString());
contact.setEmail(et_contact_email.getText().toString());
// 方式一:使用ContentResolver多次写入,每次一个字段
//addContacts(getContentResolver(), contact);
// 方式二:批处理方式
addFullContacts(getContentResolver(), contact);
} else if (view.getId() == R.id.btn_contact_query) {
}
}
// 往手机通讯录添加一个联系人信息(包括姓名、电话号码、邮箱地址)
private void addContacts(ContentResolver resolver, Contact contact) {
// 插入一条空记录
ContentValues values = new ContentValues();
// 往raw_contacts表添加联系人记录,并获取添加后的联系人编号
Uri uri = resolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(uri);
// 插入姓名
ContentValues name = new ContentValues();
// 关联联系人编号
name.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
// "姓名"的数据类型
name.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
// 联系人姓名
name.put(ContactsContract.Contacts.Data.DATA2, contact.getName());
// 往data表插入记录
resolver.insert(ContactsContract.Data.CONTENT_URI, name);
// 插入电话号码
ContentValues phone = new ContentValues();
// 关联联系人编号
phone.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
// "电话号码"的数据类型
phone.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
// 联系人电话号码
phone.put(ContactsContract.Contacts.Data.DATA1, contact.getPhone());
// 电话号码类型,1表示家庭,2表示工作
phone.put(ContactsContract.Contacts.Data.DATA2, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
// 往data表插入记录
resolver.insert(ContactsContract.Data.CONTENT_URI, phone);
// 插入邮箱地址
ContentValues email = new ContentValues();
// 关联联系人编号
email.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
// "邮箱地址"的数据类型
email.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
// 联系人邮箱地址
email.put(ContactsContract.Contacts.Data.DATA1, contact.getEmail());
// 邮箱地址类型,1表示家庭,2表示工作
email.put(ContactsContract.Contacts.Data.DATA2, ContactsContract.CommonDataKinds.Email.TYPE_WORK);
// 往data表插入记录
resolver.insert(ContactsContract.Data.CONTENT_URI, email);
}
private void addFullContacts(ContentResolver resolver, Contact contact) {
// 创建一个插入联系人主记录的内容操作器
ContentProviderOperation op_main = ContentProviderOperation
.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null) // 需要给一个空的value
.build();
// 创建一个插入联系人姓名记录的内容操作器
ContentProviderOperation op_name = ContentProviderOperation
.newInsert(ContactsContract.Data.CONTENT_URI)
// 将第0个操作的id,即raw_contacts的id作为data表中的raw_contact_id
.withValueBackReference(ContactsContract.Contacts.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.Contacts.Data.DATA2, contact.getName())
.build();
// 创建一个插入联系人电话号码记录的内容操作器
ContentProviderOperation op_phone = ContentProviderOperation
.newInsert(ContactsContract.Data.CONTENT_URI)
// 将第0个操作的id,即raw_contacts的id作为data表中的raw_contact_id
.withValueBackReference(ContactsContract.Contacts.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.Contacts.Data.DATA1, contact.getPhone())
.withValue(ContactsContract.Contacts.Data.DATA2, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
.build();
// 创建一个插入联系人邮箱地址记录的内容操作器
ContentProviderOperation op_email = ContentProviderOperation
.newInsert(ContactsContract.Data.CONTENT_URI)
// 将第0个操作的id,即raw_contacts的id作为data表中的raw_contact_id
.withValueBackReference(ContactsContract.Contacts.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.Contacts.Data.DATA1, contact.getEmail())
.withValue(ContactsContract.Contacts.Data.DATA2, ContactsContract.CommonDataKinds.Email.TYPE_WORK)
.build();
// 声明一个内容操作器的列表,并将上面四个操作器添加到该列表中
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
operations.add(op_main);
operations.add(op_name);
operations.add(op_phone);
operations.add(op_email);
try {
resolver.applyBatch(ContactsContract.AUTHORITY, operations);
} catch (OperationApplicationException | RemoteException e) {
e.printStackTrace();
}
}
}