一.前言
1.1 Android的电话本的机制.
Android的电话本通过contentProvider封装好的。我们只要通过sdk提供的Uri和字段来对其进行增、删、改、查。
1.2 权限
<uses-permission android:name="android.permission.WRITE_CONTACTS"></uses-permission> <uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
1.3 找到ContentProvider维护的Sqlist数据库文件( .db)
ContentProvider其实自己管理一个Sqlist数据库文件( .db)。这个文件的路径为/data/data/com.android.providers.contacts/databases/contacts2.db。如图:
1.4 查看ContentProvider维护的Sqlist数据库文件( .db)
在模拟器中的电话本里创建几个联系人,打开1.2中的.db文件,可以用数据库查看工具SQLite Expert Professional打开看下,如图:
二. api
从上图,可以看出左边是.db文件的表,点开各表后可以看出主要的表有raw_contacts,contacts,data
2.1 三张主表.
从api中可以看到android.provider.ContactsContract是sdk2.0的类库,从api和上面的图都可以看出关于电话本主要信息都存在
ContactsContract.Data,ContactsContract.RawContacts,ContactsContract.Contacts三张表里
2.1.1 以上三张表的关联关系.
ContactsContract.Data, ContactsContract.RawContacts, ContactsContract.Contacts 三张表的关联,
ContactsContract.RawContacts表里包含ContactsContract.Contacts的contact_id;ContactsContract.Data表里有ContactsContract.RawContacts的raw_contact_id,和ContactsContract.Contacts的contact_id
2.2 各数据对应的类库,电话本各字段的数据结构
2.2.1 Email 对应ContactsContract.CommonDataKinds.Email
Email数据有三个字段存储:ADDRESS为Email值;TYPE为类型,当为自定义(TYPE_CUSTOM)时,LABEL字段要写入用户自定义的类型;
Type Alias Data column String ADDRESSDATA1Email address itself. int TYPEDATA2Allowed values are:
TYPE_CUSTOM. Put the actual type inLABEL.TYPE_HOMETYPE_WORKTYPE_OTHERTYPE_MOBILE
String LABELDATA32.2.2 IM 对应ContactsContract.CommonDataKinds.Im
Im有5个字段
Type Alias Data column String DATADATA1int TYPEDATA2Allowed values are:
TYPE_CUSTOM. Put the actual type inLABEL.TYPE_HOMETYPE_WORKTYPE_OTHER
String LABELDATA3String PROTOCOLDATA5Allowed values:
PROTOCOL_CUSTOM. Also provide the actual protocol name asCUSTOM_PROTOCOL.PROTOCOL_AIMPROTOCOL_MSNPROTOCOL_YAHOOPROTOCOL_SKYPEPROTOCOL_QQPROTOCOL_GOOGLE_TALKPROTOCOL_ICQPROTOCOL_JABBERPROTOCOL_NETMEETING
String CUSTOM_PROTOCOLDATA62.2.3 Phone 对应ContactsContract.CommonDataKinds.Phone
2.2.4 Postal address 通讯地址 对应ContactsContract.CommonDataKinds.StructuredPostal
Type Alias Data column String NUMBERDATA1int TYPEDATA2Allowed values are:
TYPE_CUSTOM. Put the actual type inLABEL.TYPE_HOMETYPE_MOBILETYPE_WORKTYPE_FAX_WORKTYPE_FAX_HOMETYPE_PAGERTYPE_OTHERTYPE_CALLBACKTYPE_CARTYPE_COMPANY_MAINTYPE_ISDNTYPE_MAINTYPE_OTHER_FAXTYPE_RADIOTYPE_TELEXTYPE_TTY_TDDTYPE_WORK_MOBILETYPE_WORK_PAGERTYPE_ASSISTANTTYPE_MMS
String LABELDATA3
最长用的有 TYPE:类型;STREET:街道;CITY:市;REGION:省;POSTCODE:邮政编码;
Type Alias Data column String FORMATTED_ADDRESSDATA1int TYPEDATA2Allowed values are:
TYPE_CUSTOM. Put the actual type inLABEL.TYPE_HOMETYPE_WORKTYPE_OTHER
String LABELDATA3String STREETDATA4String POBOXDATA5Post Office Box number String NEIGHBORHOODDATA6String CITYDATA7String REGIONDATA8String POSTCODEDATA9String COUNTRYDATA10
三.代码
3.1根据API写代码.
在api里ContactsContract.Data 和ContactsContract.RawContacts文档里有关于insert ,update, delete,query的代码,显示出操作各自的表的代码。可以根据这些来完成我们自己的逻辑。
3.2 查询 (查出全部联系人,在只显示姓名)需要如图:

需求分析:由于列表中只需要姓名,所以在查询表时就只查询出姓名信息就好。当点击某个联系人再查出Email,phone等详细信息。
3.2.1 查询联系人总表代码:
说明:由于姓名可以直接在ContactsContract.Contacts表里查到,所以如下
public static final String[] PROJECTION_CONTACTS = { Contacts._ID, Contacts.PHOTO_ID, Contacts.IN_VISIBLE_GROUP, Contacts.HAS_PHONE_NUMBER, Contacts.DISPLAY_NAME, Contacts.CUSTOM_RINGTONE }; /** * wu0wu * * 功能:查询所有联系人PROJECTION_CONTACTS信息 * * */ public static void _getContacts(ContentResolver cr) { Cursor cursorContact = null; try { cursorContact = cr.query(ContactsContract.Contacts.CONTENT_URI, PROJECTION_CONTACTS, Contacts.IN_VISIBLE_GROUP + "=1", null, null); Log.e("wu0wu", "联系人个数=" + cursorContact.getCount()); int[] indexs = getColumnIndexs(PROJECTION_CONTACTS, cursorContact); while (cursorContact.moveToNext()) { Log.e("wu0wu", "------------------------------------"); for (int i = 0; i < PROJECTION_CONTACTS.length; i++) { String value = cursorContact.getString(indexs[i]); Log.e("wu0wu", PROJECTION_CONTACTS[i] + "=" + value); } } } catch (Exception e) { Log.e("wu0wu", e.toString()); } finally { if (cursorContact != null) { cursorContact.close(); } } } private static int[] getColumnIndexs(String[] projections, Cursor c) { int[] ret = new int[projections.length]; for (int i = 0; i < projections.length; i++) { ret[i] = c.getColumnIndex(projections[i]); } return ret; }3.2.2 根据contactId查询联系人详细
// phone private static final String[] PROJECTION_PHONENUMBER_CONTACT = { Phone.NUMBER, Phone.TYPE, Phone.LABEL }; /* DISPLAY_NAME唯一性 */ private static final String[] PROJECTION_DISPLAYNAME_CONTACT = { StructuredName.DISPLAY_NAME }; // Email private static final String[] PROJECTION_EAMIL_CONTACT = { Email.DATA1, Email.TYPE, Email.LABEL }; // IM private static final String[] PROJECTION_IM_CONTACT = new String[] { Im.DATA, Im.TYPE, Im.LABEL, Im.PROTOCOL }; // address private static final String[] PROJECTION_ADDRESS_CONTACT = new String[] { StructuredPostal.STREET, StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE, StructuredPostal.COUNTRY, StructuredPostal.TYPE, StructuredPostal.LABEL, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD, }; // Organization private static final String[] PROJECTION_ORGANIZATION_CONTACT = new String[] { Organization.COMPANY, Organization.TYPE, Organization.LABEL, Organization.TITLE }; // note private static final String[] PROJECTION_NOTES_CONTACT = new String[] { Note.NOTE }; // nickname private static final String[] PROJECTION_NICKNAMES_CONTACT = new String[] { Nickname.NAME, Nickname.TYPE, Nickname.LABEL }; // website private static final String[] PROJECTION_WEBSITES_CONTACT = new String[] { Website.URL, Website.TYPE, Website.LABEL }; /** * 功能:根据contactId查询联系人详细 * * 在android.provider.ContactsContract.Data表里查询 * */ public static void _getContactByContactId(ContentResolver cr, String contactId) { Cursor c = null; c = cr.query(Data.CONTENT_URI, null, Data.CONTACT_ID + "=?", new String[] { contactId }, null); String mimeType = null; String[] contentValue = null; ArrayList<String[]> displayNameList = new ArrayList<String[]>();// 存显示名 ArrayList<String[]> phoneList = new ArrayList<String[]>();// 存电话号码,可多个 ArrayList<String[]> emailList = new ArrayList<String[]>();// 存Email,可多个 ArrayList<String[]> imList = new ArrayList<String[]>();// 存im,可多个 ArrayList<String[]> postalList = new ArrayList<String[]>();// 存postal地址,可多个 ArrayList<String[]> organizationList = new ArrayList<String[]>();// 存organization组织,可多个 ArrayList<String[]> noteList = new ArrayList<String[]>();// 存note备注 ArrayList<String[]> nicknameList = new ArrayList<String[]>();// 存Nickname昵称 ArrayList<String[]> websiteList = new ArrayList<String[]>();// 存Website网站 while (c.moveToNext()) { // 根据mimeType分类信息 mimeType = c.getString(c.getColumnIndex(Data.MIMETYPE)); if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { contentValue = MyUtils.getStringInContactCursor(c, PROJECTION_DISPLAYNAME_CONTACT); displayNameList.add(contentValue); } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) { // 每个contentValue存一类PROJECTION_PHONENUMBER_CONTACT数据 contentValue = MyUtils.getStringInContactCursor(c, PROJECTION_PHONENUMBER_CONTACT); phoneList.add(contentValue); } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) { contentValue = MyUtils.getStringInContactCursor(c, PROJECTION_EAMIL_CONTACT); emailList.add(contentValue); } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) { contentValue = MyUtils.getStringInContactCursor(c, PROJECTION_IM_CONTACT); imList.add(contentValue); } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) { contentValue = MyUtils.getStringInContactCursor(c, PROJECTION_ADDRESS_CONTACT); postalList.add(contentValue); } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) { contentValue = MyUtils.getStringInContactCursor(c, PROJECTION_ORGANIZATION_CONTACT); organizationList.add(contentValue); } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)) { contentValue = MyUtils.getStringInContactCursor(c, PROJECTION_NOTES_CONTACT); noteList.add(contentValue); } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) { contentValue = MyUtils.getStringInContactCursor(c, PROJECTION_NICKNAMES_CONTACT); nicknameList.add(contentValue); } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) { contentValue = MyUtils.getStringInContactCursor(c, PROJECTION_WEBSITES_CONTACT); websiteList.add(contentValue); } } c.close(); // log MyUtils.logContactsDetails("displayName",PROJECTION_DISPLAYNAME_CONTACT, displayNameList); MyUtils.logContactsDetails("phoneNumber",PROJECTION_PHONENUMBER_CONTACT, phoneList); MyUtils.logContactsDetails("Email", PROJECTION_EAMIL_CONTACT,emailList); MyUtils.logContactsDetails("IM", PROJECTION_IM_CONTACT, imList); MyUtils.logContactsDetails("Address", PROJECTION_ADDRESS_CONTACT,postalList); MyUtils.logContactsDetails("Organization",PROJECTION_ORGANIZATION_CONTACT, organizationList); MyUtils.logContactsDetails("Note", PROJECTION_NOTES_CONTACT, noteList); MyUtils.logContactsDetails("NickName", PROJECTION_NICKNAMES_CONTACT,nicknameList); MyUtils.logContactsDetails("WebSit", PROJECTION_WEBSITES_CONTACT,websiteList); }用到的两个方法:
public static String[] getStringInContactCursor(Cursor c, String[] projection) { String[] contentValue = new String[projection.length]; for (int i = 0; i < contentValue.length; i++) { String value = c.getString(c.getColumnIndex(projection[i])); if (value == null) { contentValue[i] = ""; } else { contentValue[i] = value; } } return contentValue; } public static void logContactsDetails(String title, String[] projection, ArrayList<String[]> data) { Log.e("wu0wu", "--------" + title + "--------"); for (int i = 0; i < data.size(); i++) { for (int j = 0; j < data.get(i).length; j++) { Log.e("wu0wu", projection[j] + "=" + data.get(i)[j]); } } }3.3 新建联系人
接口方法:
/** * 新建联系人的接口 * * @param String * accountName,accountType 为账号名账号类型,一般为NULL * @throws RemoteException * @throws OperationApplicationException */ public static String _insertContact(ContentResolver cr, String accountName, String accountType, String displayName, ArrayList<String[]> phone, ArrayList<String[]> email, ArrayList<String[]> im, ArrayList<String[]> address, ArrayList<String[]> organization, ArrayList<String[]> notes, ArrayList<String[]> nickname, ArrayList<String[]> website) throws RemoteException, OperationApplicationException { ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); String rawId = ""; long rawContactId = insertRawContact(cr, accountName, accountType); rawId = Long.toString(rawContactId); if (displayName != null) { insertContactDisplayname(ops, StructuredName.CONTENT_ITEM_TYPE, rawId, displayName); } if (phone != null) { for (int j = 0; j < phone.size(); j++) { String[] item = phone.get(j); insertItemToContact(ops, Phone.CONTENT_ITEM_TYPE, rawId, PROJECTION_PHONENUMBER_CONTACT, item); } } if (email != null) { for (int j = 0; j < email.size(); j++) { String[] item = email.get(j); insertItemToContact(ops, Email.CONTENT_ITEM_TYPE, rawId, PROJECTION_EAMIL_CONTACT, item); } } if (im != null) { for (int j = 0; j < im.size(); j++) { String[] item = im.get(j); insertItemToContact(ops, Im.CONTENT_ITEM_TYPE, rawId, PROJECTION_IM_CONTACT, item); } } if (address != null) { for (int j = 0; j < address.size(); j++) { String[] item = address.get(j); insertItemToContact(ops, StructuredPostal.CONTENT_ITEM_TYPE, rawId, PROJECTION_ADDRESS_CONTACT, item); } } if (organization != null) { for (int j = 0; j < organization.size(); j++) { String[] item = organization.get(j); insertItemToContact(ops, Organization.CONTENT_ITEM_TYPE, rawId, PROJECTION_ORGANIZATION_CONTACT, item); } } if (notes != null) { for (int j = 0; j < notes.size(); j++) { String[] item = notes.get(j); insertItemToContact(ops, Note.CONTENT_ITEM_TYPE, rawId, PROJECTION_NOTES_CONTACT, item); } } if (nickname != null) { for (int j = 0; j < nickname.size(); j++) { String[] item = nickname.get(j); insertItemToContact(ops, Nickname.CONTENT_ITEM_TYPE, rawId, PROJECTION_NICKNAMES_CONTACT, item); } } if (website != null) { for (int j = 0; j < website.size(); j++) { String[] item = website.get(j); insertItemToContact(ops, Website.CONTENT_ITEM_TYPE, rawId, PROJECTION_WEBSITES_CONTACT, item); } } cr.applyBatch(ContactsContract.AUTHORITY, ops); return rawId; } /* * 通过往ROWCONTACT里插入数据,获得rawId * * @param cr * * @param accountName 一般为NULL * * @param accountType 一般为NULL * * @return */ private static long insertRawContact(ContentResolver cr, String accountName, String accountType) { ContentValues values = new ContentValues(); values.put(RawContacts.ACCOUNT_NAME, accountName); values.put(RawContacts.ACCOUNT_TYPE, accountType); // values.put(Contacts.DISPLAY_NAME, displayName); Uri rawContactUri = cr.insert(RawContacts.CONTENT_URI, values); long rawContactId = ContentUris.parseId(rawContactUri); return rawContactId; } private static void insertContactDisplayname( ArrayList<ContentProviderOperation> ops, String mimeType, String rawContactId, String displayName) throws RemoteException, OperationApplicationException { ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI).withValue( Data.MIMETYPE, mimeType).withValue(Data.RAW_CONTACT_ID, rawContactId).withValue(StructuredName.DISPLAY_NAME, displayName).build()); } private static void insertItemToContact( ArrayList<ContentProviderOperation> ops, String mimeType, String rawContactId, String[] PROJECTION_CONTACT, String[] item) throws RemoteException, OperationApplicationException { // ContentValues values = new ContentValues(); // values.put(Data.RAW_CONTACT_ID, rawContactId); // values.put(Data.MIMETYPE, mimeType); // for (int i = 0; i < PROJECTION_CONTACT.length; i++) { // values.put(PROJECTION_CONTACT[i], item[i]); // } // Uri dataUri = cr.insert(Data.CONTENT_URI, values); Builder builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); builder.withYieldAllowed(true); builder.withValue(Data.RAW_CONTACT_ID, rawContactId); builder.withValue(Data.MIMETYPE, mimeType); for (int i = 0; i < PROJECTION_CONTACT.length; i++) { builder.withValue(PROJECTION_CONTACT[i], item[i]); } ops.add(builder.build()); }
本文深入解析了Android电话本的实现机制,包括权限、ContentProvider、数据库结构及操作API,并提供了具体代码示例,涵盖了查询、新建联系人等功能。


3673

被折叠的 条评论
为什么被折叠?



