安卓基础之《(17)—内容提供者(3)使用内容组件获取通讯信息》

一、利用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();
        }

    }
}
相关推荐
游戏开发爱好者82 小时前
日常开发与测试的 App 测试方法、查看设备状态、实时日志、应用数据
android·ios·小程序·https·uni-app·iphone·webview
王码码20353 小时前
Flutter for OpenHarmony 实战之基础组件:第三十一篇 Chip 系列组件 — 灵活的标签化交互
android·flutter·交互·harmonyos
黑码哥3 小时前
ViewHolder设计模式深度剖析:iOS开发者掌握Android列表性能优化的实战指南
android·ios·性能优化·跨平台开发·viewholder
亓才孓3 小时前
[JDBC]元数据
android
独行soc3 小时前
2026年渗透测试面试题总结-17(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
金融RPA机器人丨实在智能3 小时前
Android Studio开发App项目进入AI深水区:实在智能Agent引领无代码交互革命
android·人工智能·ai·android studio
科技块儿3 小时前
利用IP查询在智慧城市交通信号系统中的应用探索
android·tcp/ip·智慧城市
独行soc4 小时前
2026年渗透测试面试题总结-18(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
王码码20354 小时前
Flutter for OpenHarmony 实战之基础组件:第二十七篇 BottomSheet — 动态底部弹窗与底部栏菜单
android·flutter·harmonyos
2501_915106324 小时前
app 上架过程,安装包准备、证书与描述文件管理、安装测试、上传
android·ios·小程序·https·uni-app·iphone·webview