Android ContentProvider 获取和插入联系人

来源:互联网 发布:烟花算法 编辑:程序博客网 时间:2024/04/30 02:24

项目简介:

该项目为获取手机联系人,插入手机联系人。

详细介绍:

该应用共三个按钮,一个ListView
当用户点击“获取所有联系人”按钮时,该应用将会查找到所有的联系人,显示在ListView上
当用户点击“添加联系人(不安全)”按钮时,该应用将会自动添加一个联系人到手机中,但是如果遇到突发情况,程序可能执行到一半,联系人信息可能插入不全。如在插入联系人姓名、电话、email时候,当插入姓名后断电,将会导致插入的联系人只有姓名,没有电话、email。
当用户点击“添加联系人(安全)”按钮时,该应用自动添加一个联系人到手机中。该方法是安全的,即使遇到突发情况,要么联系人信息全部插入到数据库中,要么就没有插入任何信息。不存在只插入一般信息的情况。

该应用涉及到的知识有:

  • 1.联系人三张主要的表:
      raw_contacts表: 主要存放联系人的id
        contact_id:联系人的id
      data表:存放联系人的详细信息。该表的每一行都存储联系人的一个信息
        data1:联系人具体的信息
        raw_contact_id:该行信息所属联系人得id
        mimetype_id:该行属于什么信息
      mimetypes表:

  • 2.如何获取手机联系人
      首先在raw_dontacts表中获取联系人的id,即contact_id字段的值
    然后根据联系人的id在data表中查询联系人的信息,即查询条件是raw_contact_id=?
      注意的是:
      在这里,Android系统查询data表的时候,该表中的 mimetype_id 是无法查询到了的,而是官方直接在该内容提供者的查询方法中使用了关联查询,把data表中的mimetype_id先查询到,然后再到mimetypes表中查询到该数字对应的mimetype字段的值,然后封装在一个Cursor对象中。所以,直接在data表中查询mimetype字段即可。
      

  • 3.如何插入联系人
    实际上就是查询联系人的逆向工程。
    即首先在raw_dontacts表中插入一个联系人,获取该联系人的id
    然年再在data表中插入具体的信息

  • 4.删除联系人
      删除联系人是十分简单的。只要把raw_dontacts表中的联系人的id(即contact_id字段)变为null即可,这样系统就无法查询到联系人了。但是联系人的信息仍然保存在data表中。这其实就是Android系统的机制。
      所以,从旧手机中获取联系人信息就是这个原理,因为data表信息是一直存在的

  • 5.如何获取联系人的ContentProvider的Uri
      在Android源码下按照该路径:       packages\providers\ContactsProvider,然后打开清单文件,就可以找到ContactsProvider2(这里有个2,说明之间有个1,但是2更好用,所以1就退出了历史舞台),获取该内容提供者的一些信息。再根据:src\com\android\providers\contacts寻找到ContactsProvider2.java 文件,打开后搜索“addUri”,即可寻找到UriMatcher的匹配项,即可知道该内容提供者的Uri

注意:

  • 1.查询联系人,写入联系人都是需要权限的
  • 2.最好现在当前项目先用Android的单元测试先对自己的代码测试一下,然后再到Activity中去执行,避免重复部署浪费时间

步骤:

1.创建一个Android应用。

接下来要用从手机中读取联系人,为了方便,创建一个javaBean,即person类:

public class Person {    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 [_id=" + name + ", phone=" + phone + ", email=" + email + "]";    }}

2.布局文件

编写activity_main.xml文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context="hhh.exercise.hcontentprovider_d.MainActivity" >    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@android:color/black"        android:onClick="click1"        android:text="获取所有联系人"        android:textColor="#00ff00"        android:textSize="30sp" />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@android:color/black"        android:onClick="click2"        android:text="添加联系人(不安全)"        android:textColor="#00ff00"        android:textSize="30sp" />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@android:color/black"        android:onClick="click3"        android:text="添加联系人(安全)"        android:textColor="#00ff00"        android:textSize="30sp" />    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content" >        <TextView            android:id="@+id/tv_name"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:text="Name"            android:textColor="#ff0000" />        <TextView            android:id="@+id/tv_phone"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:text="Phone"            android:textColor="#00ff00" />        <TextView            android:id="@+id/tv_email"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:text="Email"            android:textColor="#000000" />    </LinearLayout>    <ListView        android:id="@+id/lv"        android:layout_width="match_parent"        android:layout_height="wrap_content" >    </ListView></LinearLayout>

这个布局就是三个按钮,分别用来获取联系人,以及两种添加联系人的方式。按钮下面是三个TestView,显示一些必要的信息,在下面就是一个ListView,用于将查找到的联系人显示在上面,布局界面如下:

这里写图片描述

既然布局中有ListView,那么就要写另一个布局文件,用来填充ListView:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal" >    <TextView        android:id="@+id/tv_name"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:text="Name"        android:textColor="#ff0000" />    <TextView        android:id="@+id/tv_phone"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:text="Phone"        android:textColor="#00ff00" />    <TextView        android:id="@+id/tv_email"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:text="Email"        android:textColor="#000000" /></LinearLayout>

界面如下所示:

这里写图片描述

其实就是三个TestView,用来显示联系人的信息

3.Activity

MainActivity 的代码如下:

import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import android.app.Activity;import android.content.ContentProviderOperation;import android.content.ContentResolver;import android.content.ContentValues;import android.content.OperationApplicationException;import android.content.ContentProviderOperation.Builder;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.os.RemoteException;import android.view.View;import android.widget.ListView;import android.widget.SimpleAdapter;import hhh.exercise.domain.Person;public class MainActivity extends Activity {    private ListView lv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        lv = (ListView) findViewById(R.id.lv);    }    /**     * 查询手机中所有的联系人,并显示在列表上。     *      * 查询分为两步:1、往raw_contacts表中查询到联系人的id。 2、根据查询到的联系人id到data表中查找数据     */    public void click1(View view) {        // 创建一个List,存储所有的联系人        List<Person> list = new ArrayList<Person>();        ContentResolver resolver = getContentResolver();        // 从raw_contacts表中查询到联系人的id        Cursor cursorContactId = resolver.query(Uri.parse("content://com.android.contacts/raw_contacts"),                new String[] { "_id" }, null, null, null);        while (cursorContactId.moveToNext()) {            // 获取联系人的id            String _id = cursorContactId.getString(cursorContactId.getColumnIndex("_id"));            // 根据联系人的id在data表中查询联系人的数据            // mimetype: 内容类型。用来判断数据是什么类型的            // data1、data2:是我们想要查询的数据.其中data2是对data1的补充说明(例如:data1表示电话,data2表示的是电话的类型,可能是家庭电话、公司电话等)            /*             * 在这里,Android系统查询data表的时候,该表中的 mimetype_id             * 是无法查询到了的,而是直接在该内容提供者的查询方法中使用了关联查询,把data表中的mimetype_id先查询到, 然后再到             * mimetypes表中查询到该数字对应的mimetype字段的值,然后封装在一个Cursor对象中。所以,             * 直接在data表中查询mimetype字段即可。             * 可以直接先查询出该表所有的字段,返回一个cursor,然后使用cursor.getColumnNames()获取到该表的所有的字段             * ,即可以知道data表的查询字段可以传入那些参宿             */            Cursor cursorData = resolver.query(Uri.parse("content://com.android.contacts/data"),                    new String[] { "mimetype", "data1" }, "raw_contact_id=?", new String[] { _id }, null);            Person person = new Person();            while (cursorData.moveToNext()) {                // 获得数据的类型(数据data1是姓名、电话还是其他)                String mimetype = cursorData.getString(cursorData.getColumnIndex("mimetype"));                String data1 = cursorData.getString(cursorData.getColumnIndex("data1"));                if (mimetype.equals("vnd.android.cursor.item/phone_v2")) {                    // vnd.android.cursor.item/phone_v2表示该数据是电话                    person.setPhone(data1);                } else if (mimetype.equals("vnd.android.cursor.item/email_v2")) {                    // vnd.android.cursor.item/email_v2表示该数据是邮件                    person.setEmail(data1);                } else if (mimetype.equals("vnd.android.cursor.item/name")) {                    // vnd.android.cursor.item/name表示该数据是姓名                    person.setName(data1);                }            }            // 把联系人添加到list中            list.add(person);        }        // 列表显示联系人        show(list);    }    /**     * 添加联系人 添加联系人方法一:不安全版 需要获取通讯录的写权限     * 这个方法有些错误:添加联系人分为多个操作,可能存在中断的情况,导致数据只添加了部分。所以需要同步。     *      * 添加联系人分为两步: 1、往raw_contacts表中添加联系人的id。 2、把联系人的各项数据添加到data表中     *      * @throws Exception     */    public void click2(View view) {        ContentResolver resolver = getContentResolver();        // 1先在raw_contacts表中插入联系人的id。要获取到联系人的id,只要获取到该表中的主键的id,然后加上1就是插入的联系人的id        Cursor cursorContactId = resolver.query(Uri.parse("content://com.android.contacts/raw_contacts"), null, null,                null, null);        // 默认情况下,数据库没有数据,联系人的id就是1        int contact_id = 1;        if (cursorContactId.moveToLast()) {            // 移动到Cursor对象的末尾,拿到主键,并加一            int _id = cursorContactId.getInt(cursorContactId.getColumnIndex("_id"));            contact_id = _id + 1;        }        ContentValues values = new ContentValues();        values.put("contact_id", contact_id);        resolver.insert(Uri.parse("content://com.android.contacts/raw_contacts"), values);        // 2.把数据插入到data表中        // 添加姓名。        values.clear();        values.put("raw_contact_id", contact_id);        values.put("mimetype", "vnd.android.cursor.item/name");        values.put("data1", "lisi");        resolver.insert(Uri.parse("content://com.android.contacts/data"), values);        // 添加电话        values.clear();        values.put("raw_contact_id", contact_id);        values.put("mimetype", "vnd.android.cursor.item/phone_v2");        values.put("data1", "004");        // 指定电话的类型        values.put("data2", "2");        resolver.insert(Uri.parse("content://com.android.contacts/data"), values);        // 添加email        values.clear();        values.put("raw_contact_id", contact_id);        values.put("mimetype", "vnd.android.cursor.item/email_v2");        values.put("data1", "004@qq.com");        values.put("data2", "2");        resolver.insert(Uri.parse("content://com.android.contacts/data"), values);    }    /**     * 添加联系人方法二:安全版 在同一个事务中完成联系人各项数据的添加。     * 系统是通过内容提供者添加联系人。我们可以先把这些操作传给一个集合,再把这个集合传给内容提供者。由内容提供者内部开好事务后,在迭代这些集合中的操作,使得这些操作都在同一个事务中执行。     *      * @throws OperationApplicationException     * @throws RemoteException     *      */    public void click3(View view) throws RemoteException, OperationApplicationException {        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");        ContentResolver resolver = getContentResolver();        // 用ArrayList封装所有的操作        ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();        // 往集合里面传入操作(此时操作并没有执行,仅仅是作为一个对象传入)        // 为添加联系人操作新建对象。使用ContentProviderOperation的静态方法newInsert        Builder builder = ContentProviderOperation.newInsert(uri);        // withValue为字段赋值。可以随便找一个列设为空值或者需要设置的值。为下文获取做铺垫(account_name是谷歌的账号)        builder.withValue("account_name", null);        // 在使用build方法构建ContentProviderOperation的操作对象.得到了这个操作对象,但是并没有执行(也就是说没有完成插入操作)。这段代码仅仅是得到了操作对象而已        ContentProviderOperation op1 = builder.build();        // 添加操作对象。这个操作对象是第一个,在Arraylist集合中的位置是0号位置。        operations.add(op1);        // 往集合中添加姓名        Uri uri2 = Uri.parse("content://com.android.contacts/data");        // 无法知道上一个添加入raw_contacts表的id,所以要采用withValueBackReference获取raw_contact_id,其中0代表第一个操作对象在集合中的位置。        // 这个withValueBackReference方法的作用是:使用索引所对应的集合中的对象执行后所返回的记录的id作为第一个参数字段的值。在本题中,就是使用op1执行后所返回的id作为raw_contact_id的值。        ContentProviderOperation op2 = ContentProviderOperation.newInsert(uri2)                .withValueBackReference("raw_contact_id", 0)                .withValue("mimetype", "vnd.android.cursor.item/name")                .withValue("data2", "wangwu").build();        operations.add(op2);        // 添加电话        ContentProviderOperation op3 = ContentProviderOperation.newInsert(uri2)                .withValueBackReference("raw_contact_id", 0)                .withValue("mimetype", "vnd.android.cursor.item/phone_v2")                .withValue("data1", "005")                .withValue("data2", "2").build();        operations.add(op3);        // email        ContentProviderOperation op4 = ContentProviderOperation.newInsert(uri2)                .withValueBackReference("raw_contact_id", 0)                .withValue("mimetype", "vnd.android.cursor.item/email_v2")                .withValue("data1", "005@QQ.COM")                .withValue("data2", "2").build();        operations.add(op4);        // 使用安卓内部的API应用批量操作(即applyBatch)。第一个参数是内容提供者的主机名,第二个参数是要批量执行的操作        resolver.applyBatch("com.android.contacts", operations);    }    /**     * 列表显示联系人     */    private void show(List<Person> list) {        List<Map<String, String>> data = new ArrayList<Map<String, String>>();        for (Person person : list) {            Map<String, String> map = new HashMap<String, String>();            map.put("name", person.getName());            map.put("phone", person.getPhone());            map.put("email", person.getEmail());            data.add(map);        }        SimpleAdapter adapter = new SimpleAdapter(getApplicationContext(), data, R.layout.item,                new String[] { "name", "phone", "email" }, new int[] { R.id.tv_name, R.id.tv_phone, R.id.tv_email });        lv.setAdapter(adapter);    }}

4.清单文件

读取手机联系人和添加手机联系人都要权限的

 <uses-permission android:name="android.permission.READ_CONTACTS" />    <uses-permission android:name="android.permission.WRITE_CONTACTS" />

5.完成,测试效果

进入界面后,点击获取所有联系人这里写图片描述

点击添加联系人(不安全)和添加联系人(安全)两个按钮,没有任何反应,在点击获取所有联系人,如下所示:

这里写图片描述

可以看到两个新增的联系人的确添加进去了

0 0