当前位置: 代码迷 >> Android >> Android学习札记-22_访问通信录中的联系人和添加联系人,使用事物添加联系人.
  详细解决方案

Android学习札记-22_访问通信录中的联系人和添加联系人,使用事物添加联系人.

热度:45   发布时间:2016-05-01 11:15:53.0
Android学习笔记---22_访问通信录中的联系人和添加联系人,使用事物添加联系人...
Android学习笔记---22_访问通信录中的联系人和添加联系人,使用事物添加联系人...
22_访问通信录中的联系人和添加联系人
-----------------------------------
1.ContentProvider,十分重要:
比如短信里的信息,和呼叫记录,和图库中的图片,还有收藏夹,还有浏览器的书签都是通过
ContentProvider,从手机卡上取得手机的信息等等,都要用到ContentProvider
------------------------------------
2.在界面添加联系人:
  点击:应用程序中的通讯录应用,然后,点击menu按钮,然后添加联系人
--------------------------
3.联系人的内容是添加在一个数据库中的
  com.android.providers.contacts//联系人的相关(数据库共享者的)应用
  这个数据库文件就存在:data/data/com.android.providers.contacts/database下
  contact2.db
----------------------------------------
  com.android.contacts//联系人的相关(界面的)应用
  两者是分开的:
-----------------------------------------------
4.联系人的id,是通过这张表存的
  联系人的各项数据,联系人的电话,email等存在data表中
--------------------------------------
  这张表和data表示一对多的
-------------------------------
5.a.在data表中,通过mimetype_id来区分是什么数据,比如电话,
    姓名,email等
  b.这个mimetype_id,是引用的mimetype这个表中的id;
  c.data表中的部分字段
    data2用来存,电话的类型,比如家庭电话,手机等
    data1用来存手机号,姓和名
    data2也用来存email的类型,和名字,data3存姓
-----------------------------------------------------
  d.cells表用来存呼叫记录
---------------------------------------------
  e.raw_contacts表
    display_name是用来存放姓加名的组合,
    通过这个表的id和data表中的一个外键,就可以知道某几行属于哪个联系人的信息
----------------------------------------
  f.当data表中插入姓名的时候,才会发出update更新raw_contacts.中的姓名字段
    这个字段是用来快速的得到联系人名字的
------------------------------------------------------
3.当在界面中完成插入一个联系人以后,以及理解了三种表后,开始写代码取出相关联系人
  数据
------------------------
4.利用junit test测试
 a.新建contact项目
 b./contact/src/com/credream/contact/ContactTest.java
   package com.credream.contact;

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase;
import android.util.Log;

public class ContactTest extends AndroidTestCase
{
public static final String TAG="ContactTest";
//获取所有联系人,这时候涉及到联系人隐私,这里需要在清单文件添加权限
//需要在:android_source2.2.zip\packages\providers\ContactsProvider目录下找到清单文件,copy相应的权限,放到清单文件中
//android.permission.READ_CONTACTS
 /*<!--添加联系薄访问权限-->
 <uses-permission  android:name="android.permission.READ_CONTACTS"/>*/
public void testContact()throws Exception{
//要访问的业务提供者,在源码的
//android_source2.2.zip\packages\providers\ContactsProvider这个路径下
//打开清单文件:AndroidManifest.xml
//找到这个内容提供者:
/*   <provider android:name="ContactsProvider2"
            android:authorities="contacts;com.android.contacts"
           uri-permission android:pathPattern=".*" />
        </provider>*/
//那么怎么通过这个uri,访问内容提供者的固定的哪个数据
//可以在源码中找到这个路径下的类:ContactsProvider2
//android_source2.2.zip\packages\providers\ContactsProvider\src\com\android\providers\contacts
//   private static final ProfileAwareUriMatcher sUriMatcher =
       // new ProfileAwareUriMatcher(UriMatcher.NO_MATCH);
//由于把业务路径都封装在了这里
//可以通过sUriMatcher来查找业务路径,找到了如下路径
// static {
        // Contacts URI matching table
/*        final UriMatcher matcher = sUriMatcher;
        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
}
每一个业务路径代表什么意思呢?
通过英文单词猜测意思
matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);操作所有联系人
        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);//操作某个联系人
        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);//联系人有很多数据,操作某个联系人的数据
*/
Uri uri=Uri.parse("content://com.android.contacts/contacts");//访问通讯录中的所有的联系人
//这时候得到的是raw_contacts数据
ContentResolver resolver=getContext().getContentResolver();
Cursor cursor=resolver.query(uri, new String[]{"_id"}, null, null, null);
while (cursor.moveToNext())
{
StringBuilder sb=new StringBuilder("contactid=");
int contactid=cursor.getInt(0);
sb.append(contactid);
uri=Uri.parse("content://com.android.contacts/contacts/"+contactid+"/data");
//通过这个路径可以查询到这个联系人的所有数据
Cursor dataCursor=resolver.query(uri, new String[]{"mimetype","data1","data2"}, null, null, null);
while(dataCursor.moveToNext()){
String data=dataCursor.getString(dataCursor.getColumnIndex("data1"));
String type=dataCursor.getString(dataCursor.getColumnIndex("mimetype"));
if("vnd.android.cursor.item/name".equals(type)){//姓名数据
sb.append("name="+data);
}else if ("vnd.android.cursor.item/email_v2".equals(type)) {//email数据
sb.append("email="+data);
}else if ("vnd.android.cursor.item/phone_v2".equals(type)) {//phone数据
sb.append("phone="+data);
}
}
Log.i(TAG, sb.toString());
}
}
}
------------------------------
c./contact/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.credream.contact"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name=".ContactActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
              <uses-library android:name="android.test.runner" />
            </application>
 <instrumentation android:name="android.test.InstrumentationTestRunner"
  android:targetPackage="com.credream.contact" android:label="Tests for My App" />
 <!--添加联系薄访问权限-->
 <uses-permission  android:name="android.permission.READ_CONTACTS"/>
 </manifest>
-----------------------------
1.异常原因:
java.lang.IllegalArgumentException: URI: content://com.android.contacts/contacts1/data, calling user: com.credream.contact, calling package:com.credream.contact
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:144)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:114)
at android.content.ContentProviderProxy.bulkQueryInternal(ContentProviderNative.java:330)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:366)
at android.content.ContentResolver.query(ContentResolver.java:245)
at com.credream.contact.ContactTest.testContact(ContactTest.java:56)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:520)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1447)
-------------------------------------
原因由于:uri=Uri.parse("content://com.android.contacts/contacts"+contactid+"/data");
uri出错,正确写的应该是这样的:
uri=Uri.parse("content://com.android.contacts/contacts/"+contactid+"/data");
---------------------------
8.a.添加意志过滤器
  b.运行结果
  03-16 15:26:00.310: I/ContactTest(21022): contactid=1name=785月
03-16   15:26:00.390: I/ContactTest(21022): contactid=2phone=15355488name=李德伟  email=my@.souhu.com

---------------------------
3.通常电话来了以后,要根据电话从通讯录中,查询出这个人的姓名,然后显示这个人的名字
  在来电显示上
---------------------
3.1:根据电话,查询相关姓名的代码:
在类中添加方法,并在junit test中执行
----------------------------------------
//根据号码获取联系人的姓名
public void testContactName()throws Exception{
//根据来电号码取得业务路径
//电话是数据,所以找data//android_source2.2.zip\packages\providers\ContactsProvider\src\com\android\providers\contacts
//data/phones/filter/*这个路径就可以了,看文档也可以找到
  String phone="15355488";
Uri uri=Uri.parse("content://com.android.contacts/data/phones/filter/"+phone);
ContentResolver resolver=getContext().getContentResolver();
Cursor cursor=resolver.query(uri, new String[]{"display_name"}, null, null, null);
if(cursor.moveToFirst()){
String name=cursor.getString(0);
Log.i(TAG, name);
}
}
-------------------------------------
4.控制台执行结果
03-16 15:48:35.500: I/ContactTest(25837): 李德伟

------------------------------------------------------
5.添加联系人:代码:
//添加联系人
public void testAddContact(){
//第一步,在raw_contacts表中添加联系人id,raw_contacts代表操作data表中的数据
//第二部将联系人的各项信息添加到data表中;
Uri uri=Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolver resolver=getContext().getContentResolver();
ContentValues values=new ContentValues(); 
long contactid=ContentUris.parseId(resolver.insert(uri, values));//可以得到uri的id
//添加姓名
//得到了联系人的id
uri=Uri.parse("content://com.android.contacts/data");
values.put("raw_contact_id", contactid);
values.put("mimetype", "vnd.android.cursor.item/name");
values.put("data2", "zhangxiaoxiao");
//insert方法内容,先根据mimetype在mimetypes表中查找到相关数据,然后
//再插入到data表中
resolver.insert(uri, values);
//uri
//resolver.insert(uri, values)相当于得到了一个含有id的uri
//content://com.android.contacts/raw_contacts/10号码,就是他的id
//添加电话
values.clear();
values.put("raw_contact_id", contactid);
values.put("mimetype", "vnd.android.cursor.item/phone_v2");
values.put("data2", "2");
values.put("data1", "13676548");
resolver.insert(uri, values);
//添加email
values.clear();
values.put("raw_contact_id", contactid);
values.put("mimetype", "vnd.android.cursor.item/email_v2");
values.put("data2", "2");
values.put("data1", "[email protected]");
resolver.insert(uri, values);
//vnd.android.cursor.item/email_v2
}
-------------------------------------------------
6.使用:
//在同一个事物中完成联系人各项事物的添加
public void testAddContact2()throws Exception{
Uri uri=Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolver resolver=getContext().getContentResolver();
ArrayList<ContentProviderOperation> operations=new ArrayList<ContentProviderOperation>();
ContentProviderOperation opi=ContentProviderOperation.newInsert(uri).withValue("account_name", null).build();
//这个的作用是添加谷歌的账号,如果有的话,可以绑定谷歌的账号
operations.add(opi);
uri=Uri.parse("content://com.android.contacts/data");
ContentProviderOperation op2=ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id", 0)//使用集合中0索引所对应的操作对象,执行后返回的记录id,作为这个值
//使用索引为0的对象,执行插入后,返回的id值作为这个值
.withValue("mimetype", "vnd.android.cursor.item/name")
.withValue("data2", "创梦")
.build();
operations.add(op2);
 
ContentProviderOperation op3=ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id", 0)//使用集合中0索引所对应的操作对象,执行后返回的记录id,作为这个值
//使用索引为0的对象,执行插入后,返回的id值作为这个值
.withValue("mimetype", "vnd.android.cursor.item/phone_v2")
.withValue("data1", "15066659414")
.withValue("data2", "2")
.build();
operations.add(op3);
ContentProviderOperation op4=ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id", 0)//使用集合中0索引所对应的操作对象,执行后返回的记录id,作为这个值
//使用索引为0的对象,执行插入后,返回的id值作为这个值
.withValue("mimetype", "vnd.android.cursor.item/email_v2")
.withValue("data1", "[email protected]")
.withValue("data2", "2")
.build();
operations.add(op4);
resolver.applyBatch("com.android.contacts", operations);
//begin()//底层的实现,开始事物
/*for(op:options){//把操作迭代出来
cp.app//执行每一个操作
}*/
//然后事物提交
}
}
--------------------------------------------------------------------------
7.分别用junit执行然后,查看结果.
  对两种实现联系人添加的方式,进行底层的比较
-----------------------------------------------------

  相关解决方案