`

使用ContactsContract API

 
阅读更多
  1. Contact API的结构和使用方法

和老的API有所不同的是,在新的Contacts API中,联系人数据被安排三个主要的表中:contacts, raw contacts和 data。这种新的结构可以使系统更加容易储存和管理从多个数据源得到的特定的联系人数据。结构如下图所示:



 

1) Data表:储存所有与RawContract相关具体的信息。表的每一条记录对应一个特定信息,如:名字、电话,email地址, 头像和组信息等。每一记录通过一个mimetype_id字段来表明该行所记录的数据类型。例如,如果row的数据类型是Phone.CONTENT_ITEM_TYPE,那么第一个字段应该保存电话号码,如果数据类型为Email.CONTENT_ITEM_TYPE,那么这一记录的字段应该保存邮件地址。

Data表的字段主要有:

 

mimetype_id 表示该行存储的信息的类型
raw_contact_id 表示该行所属的RawContact
is_primary 多个data数据组成一个raw contact,该字段表示此data是否是其所属的raw contact的主data,即其display name会作为raw contact的display name
is_super_primary 该data是否是其所属的contact的主data,如果is_super_primary为1则is_primary一定为1
data1~data15 15个数据字段,对于不同类型的信息,表示不同的含义,ContactsContract.CommomDataKinds类中定义了与常用的数据类型相对应的一些类,这些类中分别定义了相应数据类型中这些字段表示的含义。一般data1表是主信息(如电话,Email地址等),data2表示副信息,data15表示Blob数据。
data_sync1~data_sync4 sync_adapter要用的字段(sync_adapter用于数据的同步,比如你手机中的Gmail帐户与Google服务器的同步)。
data_version 数据的版本,用于数据的同步。

 

2)RawContact表中的一行存储Data表中一些数据行的集合及一些其他的信息,表示一个联系人某一特定帐户的信息,比如Facebook或Exchange的一个联系人。
当插入一个raw contact或当一个raw contact所属的一个data改变时,系统会检查这个raw contact跟其他的raw contact是否可以匹配(比如如果两个raw contact的data包含相同的电话号码或名字),如果匹配他们就会被综合到一起,也就是说他们会属于同一个cantact,表现为在RawContact表中他们引用的cantact_id是一样的。
联系人姓名、组织、电话号码、Email或昵称的改变会引发raw contact的重新聚合。有两个方法控制聚合的行为Aggregaton Mode与ContactsContract.AggregationExceptions。

Aggregaton Mode:
RawContact表中有一个字段aggregation_mode,通过向特定raw contact行中插入这个字段可以修改系统对这个raw contact的聚合行为,其允许的值如下:AGGREGATION_MODE_DEFAULT:正常模式,允许自动聚合;
AGGREGATION_MODE_DISABLE:不允许聚合;
AGGREGATION_MODE_SUSPENDED:当一个raw contact的aggregation mode修改为suspended时,如果其已是一个已聚合的contact的一部分,那么它仍会保持与原来聚合到一起的raw contact的关系,即使它已改变不再跟其他raw contact匹配。AGGREGATIONEXCEPTIONS:

在数据库中存在一个表:agg_exceptions。通过字段raw_contact_id1、raw_contact_id2、mode存储两个raw contact聚合的方法,系统定义的聚合行为有3个:
TYPE_AUTOMATIC=0  由系统决定聚合行为,默认值。
TYPE_KEEP_SEPARATE=2  不聚合
TYPE_KEEP_TOGETHER=1  聚合

3) Contact表中的一行表示一个联系人,它是RawContact表中的一行或多行的数据的组合,这些RawContact表中的行表示同一个人的不同的帐户信息。Contact中的数据由系统组合RawContact表中的数据自动生成。不可以直接向这个表中插入数据,当一个raw contact被插入的时候,系统会首先查找Contact表看是否有记录跟插入的raw contact表示同一个人,如果找到了,则把找到的这个contact的_ID插入raw contact记录的CONTACT_ID字段,如果没有找到,则系统自动插入一个Contact记录并把它的_ID插入新插入的raw contact的CONTACT_ID列。


Contact表中只有TIMES_CONTACTED、LAST_TIME_CONTACTED、STARRED、CUSTOM_RINGTONE、SENE_TO_VOICEMAIL列可更改,这些列的更改会导致相应的raw contact被更改。

数据删除
    Contacts文档说:
    Be careful with deleting Contacts! Deleting an aggregate contact deletes all constituent raw contacts.
    The corresponding sync adapters will notice the deletions of their respective raw contacts 
    and remove them from their back end storage.
    删除一个Contacts,会把它所包含所有raw contacts都删除掉。
    但是用下面语句居然删除不了任何Contacts.
    getContentResolver().delete(Contacts.CONTENT_URI, null, null);
    不过在删除Contacts的所有raw contacts后,Contacts也被删除了。

如果需要读取一个联系人的信息用CONTENT_LOOKUP_URI代替CONTENT_URI;

如果需要通过电话号码查找一个联系人,用PhoneLookup.CONTENT_FIILTER_URI,这个URI为这个目的进行了优化;

如果需要通过部分名字的匹配查找,用CONTENT_FILTER_URI;

如果需要通过email,address等信息查找,查找表ContactsContract.Data,结果包含contact ID,名字...

android.provider.ontactsContract.Data类

其定义如下:


 

 public final static class Data implements DataColumnsWithJoins {
        private Data() {}
        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "data");
        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/data";
    //This flag is useful (currently) only for vCard exporter in Contacts app
        public static final String FOR_EXPORT_ONLY = "for_export_only";
    /*Build a CONTENT_LOOKUP_URI style Uri for the parent ContactsContract.Contacts entry of the given ContactsContract.Data entry.*/
        public static Uri getContactLookupUri(ContentResolver resolver, Uri dataUri) {...}
    }


 

Data类定义了CONTENT_URI及CONTENT_TYPE,主要是继承了DataColumnsWithJoins接口中的字段。DataColumnsWithJoins定义如下:

protected interface DataColumnsWithJoins extends BaseColumns, DataColumns, StatusColumns,
            RawContactsColumns, ContactsColumns, ContactNameColumns, ContactOptionsColumns,
            ContactStatusColumns {
    }

该接口只是综合了一些接口,其中:

BaseColumns定义了_ID与_COUNT字段,ContentProvider查询中都要用到的字段。

DataColumns定义了与Data表中字段一一对应的常量

RawContactsColumns, ContactsColumns, ContactNameColumns, ContactOptionsColumns表示RawContact与Contact中的一些字段,定义到此类中以方便访问。

由于联系人的信息都是按类别存储到了这个Data表中,所以我们要查找联系人的信息只需要查这个表。Data类中的Data.CONTACT_ID与Data.RAW_CONTACT_ID分别表示该表项对应的联系人在Contact与RawContract表中的ID,我们只需要知道某一个联系人的contactId或rawContractId,并根据其查找的数据的类型就可以查到相应类型的信息。

Cursor c = getContentResolver().query(Data.CONTENT_URI,
new String[] {Data._ID, Phone.NUMBER, Phone.TYPE, Phone.LABEL},
Data.CONTACT_ID + "=?" + " AND "
+ Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'",
new String[] {String.valueOf(contactId)}, null);

以上代码根据联系人的contractId,并通过设置其MIMETYPE为Phone.CONTENT_ITEM_TYPE查到了该联系人的电话号码,把Data.CONTACT_ID换为Data.RAW_CONTACT_ID即可查找相应rawContactId对应的电话号码。其中用到了Phone.CONTENT_ITEM_TYPE,这是什么呢?

CommonDataKinds类

在前面讲Data表的结构时讲到,Data的data1~data15字段用于存储各类型的数据信息,那么这15个字段分别表示什么信息呢?

前面提到了,Data表中有一个mimetype_id字段,通过这个字段关联mimetypes表表示该行代表的信息类型,因为Data表中的每一行可以表示如Phone或Address等不同类型的信息,所以对于不同类型的信息,data1~data15这15列表示不同的含义,如果要靠记忆记住这15列对于特定的类型分别表示什么意义自然不行,于是Google就预定义了一些类,每一个类对应一些预先定义好的数据类型,在每个类中定义了一些语义地、方便记忆的常量,用来对应这15个字段,比如在CommonDataKinds.Email类中有如下定义

public static final String ADDRESS = DATA1;
public static final String DISPLAY_NAME = DATA4;

DATA1与DATA4为继承自DataColumns中的常量,在DataColumns中是这样定义的:

public static final String DATA1 = "data1";
public static final String DATA4 = "data4";

这样,当于们要查找Email地址时,只需要通过ContactsContract.CommonDataKinds.Email.ADDRESS引用,而不需要知道它是存储在Data表中的data1列中。

回到上一个问题,上面查询电话号码的例子中用到了Phone.CONTENT_ITEM_TYPE,即是表示CommonDataKinds.Phone.CONTENT_ITEM_TYPE,在Phone类中有如下定义

public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone_v2";

vnd.android.cursor.item/phone_v2 就是表示Data表中相应的行存储的是电话号码的信息,通过相应类型(如Phone、Email)的CONTENT_ITEM_TYPE,我们就可以指定要查询的MIMETYPE,即要查询的数据类型。

其实在上面的例子中还有更简单的方法:

 

Cursor phone = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[] { CommonDataKinds.Phone.NUMBER }, CommonDataKinds.Phone.CONTACT_ID + " =? ",
new String[] { String.valueOf(contactId) }, null);

 

只需把query的第一个参数处传递想查找的数据类型的相应的类中的CONTENT_URI就可以了。

 Lookup Key

在新的Contact API中,为contact引入了lookup key的概念,当你的程序需要保存对联系人的引用时,用lookup key而别用row id,lookup key是contacts表中的一列,当你有一个lookup key时,可以这样构造一个Uri:

Uri lookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);

然后用这个Uri查询:

Cursor c = getContentResolver().query(lookupUri, new String[]{Contacts.DISPLAY_NAME}, ...);
try {
c.moveToFirst();
String displayName = c.getString(0);
} finally {
c.close();
}

 

用lookup key 而不用row id的原因是因为row id容易改变,用户把原先两个联系人合并成一个或因为同步的问题都会导致row id的改变。lookup key是一个字符串,它由raw contact的标识连接组成。

使用row id会比使用lookup key效率高,你可以同时保存二者,然后联合二者生成一个lookup uri:

Uri lookupUri = getLookupUri(contactId, lookupKey)

当同时有row id跟lookup key时,系统会优先以row id查询,如果无查找结果或找到的结果跟lookup key不匹配,则再用lookup key查找。

  参考1:http://developer.android.com/resources/articles/contacts.html  

The new Contacts API introduces the notion of a lookup key for a contact. If your application needs to maintain references to contacts, you should use lookup keys instead of the traditional row ids. You can acquire a lookup key from the contact itself, it is a column on the ContactsContract.Contacts table.

   The reason for this complication is that regular contact row IDs are inherently volatile. Let's say your app stored a long ID of a contact. Then the user goes and manually joins the contact with some other contact. Now there is a single contact where there used to be two, and the stored long contact ID points nowhere.

The lookup key helps resolve the contact in this case. The key is a string that concatenates the server-side identities of the raw contacts. Your application can use that string to find a contact, regardless whether the raw contact is aggregated with others or not.


  示例代码如下:

 

package com.memo;
import java.util.ArrayList;
import android.app.ListActivity;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class Main extends ListActivity {

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Uri contactsUri=ContactsContract.Contacts.CONTENT_URI;
String[] proj1=new String[]{ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
ContactsContract.Contacts.LOOKUP_KEY};
Cursor curContacts=getContentResolver().query(contactsUri,proj1, null, null, null);
   
//declare a ArrayList object to store the data that will present to the user
ArrayList<String> contactsList=new ArrayList<String>(); 
String allPhoneNo="";
if(curContacts.getCount()>0){
while(curContacts.moveToNext()){
// get all the phone numbers if exist
if(curContacts.getInt(1)>0){
allPhoneNo=getAllPhoneNumbers(curContacts.getString(2));
}
contactsList.add(curContacts.getString(0)+" , "+allPhoneNo);
allPhoneNo="";
}
}
   
// binding the data to ListView 
setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, contactsList));
ListView lv=getListView();
lv.setTextFilterEnabled(true);

}

/**
 * Get all the phone numbers of a specific contact person
 * 
 * @param lookUp_Key lookUp key for a specific contact
 * @return a string containing all the phone numbers
 */
public String getAllPhoneNumbers(String lookUp_Key){
String allPhoneNo="";

// Phone info are stored in the ContactsContract.Data table 
Uri phoneUri=ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String[] proj2={ContactsContract.CommonDataKinds.Phone.NUMBER};
// using lookUp key to search the phone numbers
String selection=ContactsContract.Data.LOOKUP_KEY+"=?";
String[] selectionArgs={lookUp_Key};
Cursor cur=getContentResolver().query(phoneUri,proj2,selection, selectionArgs, null);
while(cur.moveToNext()){
allPhoneNo+=cur.getString(0)+" ";
}
return allPhoneNo;

}



} 

 

5. 注意事项:

  1)由于要读取联系人信息,所以必须在AndroidMenifest.xml里加入相关uses-permission用于请求使用许可:

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

      参考1:http://developer.android.com/guide/topics/security/security.html 

      参考2:http://www.higherpass.com/Android/Tutorials/Working-With-Android-Contacts/

  2) 怎样获取特定联系人的电话号码?由于使用新的API,所以编写方法与使用旧的API不同。

      在查询是主要使用到lookUp key 的概念。 

      参考:http://stackoverflow.com/questions/4729551/contactscontract-lookup-phone-number-by-contact-id

来自:http://blog.163.com/hesky_fly/blog/static/732868692011102311551131/
 
 
  • 大小: 12.7 KB
分享到:
评论

相关推荐

    Android高级应用源码-Android导入导出txt通讯录工具.zip

    1. 使用ContactsContract API操作联系人。 2. 文件I/O操作,包括读取TXT文件和写入TXT文件。 3. 使用ContentResolver进行数据的增删查改。 4. 运行时权限管理,尤其是WRITE_EXTERNAL_STORAGE和READ_CONTACTS权限的...

    Android2.0 中读取联系人——ContactsContract

    在Android 2.0及更高版本中,由于SDK对联系人数据的访问方式进行了更新,开发者需要使用新的API来读取和操作联系人信息。本文将详细介绍如何使用`ContactsContract`类来实现这一功能。 在Android 2.0之前,开发人员...

    ContactsContract读取联系人的异步方法

    在Android系统中,ContactsContract是用于访问和操作设备上联系人数据的核心API。这篇博客“ContactsContract读取联系人的异步方法”深入探讨了如何利用这个API以非阻塞的方式获取用户联系人信息,这对于提高应用...

    android中ContactsContract获取联系人的方法

    这篇博客文章《android中ContactsContract获取联系人的方法》深入探讨了如何使用这个API来有效地获取和操作Android设备上的联系人数据。 首先,我们需要理解`ContactsContract`类的结构。它定义了一系列的常量和Uri...

    Pro Android学习:联系人API

    首先,Android的联系人API分为两大部分:原始联系人API(Legacy Contact API)和ContactsContract API。前者主要用于Android 2.3(Gingerbread)及更早版本,后者则从Android 3.0(Honeycomb)开始引入,是目前主流...

    联系人API 示例

    联系人API通过`ContactsContract`类提供的内容提供者来访问联系人数据。`ContactsContract`包含了各种常量和类,用于构建URI,定义数据表和字段,以及执行查询和操作。例如,`ContactsContract.Contacts`用于访问...

    Android-ContactsContract联系人增删改查

    在Android系统中, ContactsContract是访问和操作设备上联系人数据的核心API。它提供了一种统一的方式来与系统联系人数据库进行交互,无论这些联系人来自哪个应用或服务。本篇文章将深入探讨如何利用...

    使用android电话API

    总结,使用Android电话API涉及电话状态获取、短信服务管理和电话拨出,开发者需要熟悉TelephonyManager、SmsManager、Intent等关键类,并掌握权限管理和多版本兼容策略。在实际应用中,这将帮助我们构建功能丰富的...

    ContactsDemo

    总的来说,ContactsDemo项目涵盖了Android开发中的多个关键知识点,包括Content Provider、ContactsContract API、权限管理、UI设计、数据查询、性能优化以及版本兼容性处理。掌握这些技能对于一个Android开发者来说...

    安卓手机通讯录

    5. **ContactsContract API**: 安卓提供了一套标准的ContactsContract API,用于访问和操作系统通讯录。开发者可以通过这个API来读取、添加、修改和删除联系人。 6. **UI设计与布局**: 使用XML布局文件创建用户界面...

    Android导出联系人邮箱发送

    1. Android ContentProvider和ContactsContract API的使用,用于获取和操作联系人数据。 2. JavaMail API的运用,实现邮件发送,包括设置邮件头信息和添加附件。 3. Android运行时权限的动态申请,确保应用在发送...

    Android读取联系人代码示例

    如果需要处理联系人分组,可以使用ContactsContract.Groups类,而如果要处理联系人的照片,可以利用ContactsContract.CommonDataKinds.Photo类。 总之,Android提供了完善的API来访问和操作联系人数据。理解并熟练...

    Android读取通讯录中设置邮件的联系人

    综上所述,实现"Android读取通讯录中设置邮件的联系人"的功能涉及到Android的权限管理、ContentResolver、ContactsContract API的使用,以及数据处理和性能优化等多个方面。通过合理的编程实践,可以创建一个高效且...

    基于android studio的读取联系人并可点击拨打电话

    这个项目,"基于android studio的读取联系人...总结来说,这个项目涉及了Android的权限管理、ContentResolver、ContactsContract API、数据查询、UI交互以及Intent的使用。它是学习Android基础功能和实践应用的好例子。

    获取本地联系人的头像并显示

    综上所述,获取本地联系人的头像并显示涉及了Android系统的ContentResolver、ContactsContract API、图片处理和UI显示等多个环节。开发者需要理解这些基本概念,并结合具体应用场景进行优化,以提供流畅的用户体验。...

    网易邮箱大师界面实现.zip

    - **ContactsContract API**:如果应用支持查看联系人,可能使用了 Android 的 ContactsContract API 来获取和展示设备上的联系人信息。 - **Adapter**:联系人列表也需要一个适配器,将数据绑定到 RecyclerView ...

    android 应用程序源码

    通过分析这些源码,开发者不仅可以学习到Android的基本组件和API使用,还能深入理解数据存储、网络通信、用户界面设计、多媒体处理等多个方面,对提升Android开发技能大有裨益。每个应用的源码都是一个实战案例,...

    添加手机联系人信息到特别关心列表或者常用联系人

    综上所述,实现“添加手机联系人信息到特别关心列表或者常用联系人”的功能,主要涉及Android系统的ContactsContract API的使用、数据库操作以及权限管理。通过这些技术,用户可以方便地管理和组织他们的联系人,...

    android 联系人详解

    总结来说,Android的联系人管理是一个涉及到ContentProvider、ContentResolver和ContactsContract的重要领域,开发者需要理解和熟练使用这些API来实现高效的联系人操作。同时,理解权限管理和最佳实践是确保应用能...

    Android使用ContentProvide实现的建议联系人读取(7.11环境)

    为了确保跨版本兼容性,开发者应遵循Android的版本适配策略,比如使用Support Library或者AndroidX库,以及使用Build.VERSION.SDK_INT检查API级别来使用相应的API。此外,对于Android 10及以上版本,还需注意存储...

Global site tag (gtag.js) - Google Analytics