安卓基础之《(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();
        }

    }
}
相关推荐
故事不长丨2 小时前
安卓相机开发:Camera、Camera2与CameraX的使用对比及选型指南
android·相机·camera·camerax·camera2·移动设备·相机开发
_李小白3 小时前
【Android 美颜相机】第七天:GLTextureView 解析
android·数码相机
honortech3 小时前
Android studio中配置gradle和对应的AGP版本
android·ide·android studio
廋到被风吹走3 小时前
【数据库】【MySQL】事务隔离深度解析:MVCC 实现与幻读解决机制
android·数据库·mysql
AC赳赳老秦3 小时前
技术文档合著:DeepSeek辅助多人协作文档的风格统一与内容补全
android·大数据·人工智能·微服务·golang·自动化·deepseek
赛恩斯3 小时前
安卓构建工具D8和R8的区别
android
—Qeyser3 小时前
Flutter CustomScrollView 自定义滚动视图 - 完全指南
android·flutter·ios
鸣弦artha4 小时前
Flutter 框架跨平台鸿蒙开发 —— Image Widget 图片处理:圆角、裁剪、阴影
android·flutter·harmonyos
—Qeyser4 小时前
Flutter ListView 列表组件完全指南
android·flutter·ios
独自破碎E4 小时前
包含min函数的栈
android·java·开发语言·leetcode