实际应用,“通讯录” 数据读取、添加联系人信息
** 查看通讯录数据库:
首先启动模拟器、再打开 “File Explorer” 视图,
依次展开:data -- data -- com.android.providers.contacts(注意和 com.android.contacts 区分) -- databases,如图:
点击右上角的导出图标,如图:
将数据库文件导出到电脑磁盘上。
然后,你需要拥有一个能打开 SQLite 数据库的工具。例如我用的 SQLite Expert Personal。可以在网上搜索下载。
打开数据库后,就比较纠结了,需要观察表结构。如果确实看得比较纠结,干脆甩开,以免打击学习激情。
或者一狠心,自己写一个管理联系人的程序,弄个自己的通讯录数据库。
raw_contacts 表: 联系人 ID
data表 : 联系人的数据表。通过raw_contacts_id 与 raw_contacts 表联系。
存放的联系人信息如:手机号、姓名、Email 等。
联系人的每一项数据,都会在 data 表产生一条记录。例如,记录手机号的是单独一条记录,记录 Email 的是单独一条记录。并且都用 data1 字段来存放。
也就是说,联系人的所有重要信息,都以单独记录的方式,保存在这张表里的 data1字段。用 raw_contacts_id 来描述信息所属的联系人。用 mimitype_id 来描述 data1 字段存储的数据类型(是 Email 数据么?手机号数据么?座机数据么?)。
假如data1要存放的数据是 “由几个数据组合起来的” ,例如:姓名中的 FirstName 和 LastName。
那么就往后存到 data2、data3 里面。组合起来的完整姓名存放在 data1。
区分这条数据到底是短信数据、电话数据、Emai等,则是依靠 mimitype_id 字段来区分。
mimitype_id 其实是 mimitypes 的外键。观察此表得知:1 表示 email、6 表示姓名、5 表示电话号码。
data2:也用来说明 data1。例如,如果是此记录记录的数据是电话号码,那么若是住宅电话,此字段为1;手机号码为2;单位电话为3......
这些字段的意义很重要,建议读者花10分钟大致看一眼(强烈建议不要花太多时间去研究这个,意义不大),再结合后面的程序,相信会很容易理解。
既然知道了数据存放的方式了,知道它们的字段的名称了,表名也知道了。(当然,编程时我们尽量使用 Android 提供的常量。否则也许今天写的程序,用到了某某字段,睡一觉起来 Android 升完级,不认了。)
好了,sql 语句的几个要素都成立了。要存取联系人好了做吧。
当然啦,这个过程要通过 “联系人” 对外提供的接口来完成,毕竟这个程序是人家的,这个表也是人家的,我们不能直接访问第三方程序的数据库。要提供这个接口的方式可多了,ContentProvider 无疑是最佳的选择。
说穿了,其实就是我们在我们的程序中,组拼出我们需要的 sql 语句,通过 ContentProvider 通信机制,将 sql 语句送到 “联系人”程序去执行而已。并不神奇和复杂,不是么?
calls表:存放的呼叫记录。在《Android--删除某联系人的通话记录》中需要操作它
源码在:
${对应版本SDK源码目录}/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java
可以通过查看源码,获知 Uri
...
matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
...
要按照电话号码获取某一个联系人可以使用:
Uri uri = Uri.parse("content://com.android.contacts/data/phones/filter/151016899999");
** 示例代码:(上代码之前,最好的建议是:边看这个例子,边打开数据库和源码瞅瞅,对 ContentProvider 的理解会深入很多)
1. AndroidManifest.xml 加入权限
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
2.
- package wjh.android.contact;
- import java.util.ArrayList;
- import android.content.ContentProviderOperation;
- import android.content.ContentProviderResult;
- import android.content.ContentResolver;
- import android.content.ContentUris;
- import android.content.ContentValues;
- import android.database.Cursor;
- import android.net.Uri;
- import android.provider.ContactsContract;
- import android.provider.ContactsContract.RawContacts;
- import android.provider.ContactsContract.CommonDataKinds.Email;
- import android.provider.ContactsContract.CommonDataKinds.Phone;
- import android.provider.ContactsContract.CommonDataKinds.StructuredName;
- import android.provider.ContactsContract.Contacts.Data;
- import android.test.AndroidTestCase;
- import android.util.Log;
- /**
- * 通讯录操作示例
- *
- */
- public class ContactTest extends AndroidTestCase {
- private static final String TAG = "ContactTest";
- /**
- * 获取通讯录中所有的联系人
- */
- public void testGetContacts() throws Throwable {
- ContentResolver contentResolver = this.getContext().getContentResolver();
- String uriStr = "content://com.android.contacts/contacts";
- Uri uri = Uri.parse(uriStr);
- Cursor cursor = contentResolver.query(uri, null, null, null, null);
- // 遍历联系人
- while (cursor.moveToNext()) {
- // 联系人 ID
- int contactId = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID));
- // 联系人显示名称
- String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
-
- // 联系人电话号码需要对另一个表进行查询,所以用到另一个 uri:content://com.android.contacts/data/phones
- Cursor phones = getContext().getContentResolver().query(
- ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
- null,
- ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " // 根据上一步获取的联系人 id 来查询
- + contactId, null, null);
- String phone = "";
- while (phones.moveToNext()) {
- // "data1"字段,所以此处也可以直接写 "data1",但不推荐
- phone = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
- }
-
- // 再查询 Email。uri 为 : content://com.android.contacts/data/emails
- Cursor emails = getContext().getContentResolver().query(
- ContactsContract.CommonDataKinds.Email.CONTENT_URI,
- null,
- ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = "
- + contactId, null, null);
- while (emails.moveToNext()) {
- /* 一样是 "data1" 字段。现在明白了吧?一个联系人的信息,其实被分成了好几条记录来保存,data1分别保存了各种重要的信息。
- * 是时候参照打开数据库我前面所说,去瞄一眼它的表结构了!
- */
- String emailAddress = emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
- Log.i("RongActivity", "emailAddress=" + emailAddress);
- }
- emails.close();
- Log.i(TAG, "Contact [contactId= "+ contactId +"name=" + name + ", phone=" + phone + "]");
- }
- }
-
- /**
- * 首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
- * 这时后面插入data表的依据,只有执行空值插入,才能使插入的联系人在通讯录里面可见
- */
- public void testInsert() {
- ContentValues values = new ContentValues();
- //首先向RawContacts.CONTENT_URI执行一个空值插入(raw_contacts 表), 为了获取生成的联系人 ID
- Uri rawContactUri = this.getContext().getContentResolver().insert(RawContacts.CONTENT_URI, values);
-
- //然后获取系统返回的rawContactId , 就是新加入的这个联系人的 ID
- long rawContactId = ContentUris.parseId(rawContactUri);
-
- /* Andorid 中,将联系人的姓名、电话、Email
- * 分别存放在 data 表的同一个字段的三条记录当中
- * 因此要 Insert 三次 */
-
- //往data表入姓名数据
- values.clear();
-
- // raw_contacts_id 字段,是 raw_contacts表id 的外键,用于说明此记录属于哪一个联系人
- values.put(Data.RAW_CONTACT_ID, rawContactId);
-
- // mimitype_id 字段,用于描述此数据的类型,电话号码?Email?....
- values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); // 注意查看第二个参数的常量值
-
- values.put(StructuredName.GIVEN_NAME, "文白菜"); // 这个名字真好听
- this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
-
- //往data表入电话数据
- values.clear();
- values.put(Data.RAW_CONTACT_ID, rawContactId);
-
- values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
- values.put(Phone.NUMBER, "15101689230");
- values.put(Phone.TYPE, Phone.TYPE_MOBILE);
- this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
-
- //往data表入Email数据
- values.clear();
- values.put(Data.RAW_CONTACT_ID, rawContactId);
- values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
- values.put(Email.DATA, "[email protected]");
- values.put(Email.TYPE, Email.TYPE_WORK);
- this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
- }
- /**
- * 在同一个事务当中保存联系人
- */
- public void testSave() throws Throwable{
- //文档位置:reference/android/provider/ContactsContract.RawContacts.html
- ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
-
- int rawContactInsertIndex = ops.size();
- ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
- .withValue(RawContacts.ACCOUNT_TYPE, null)
- .withValue(RawContacts.ACCOUNT_NAME, null)
- .build());
- //文档位置:reference/android/provider/ContactsContract.Data.html
- ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
- .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
- .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
- .withValue(StructuredName.GIVEN_NAME, "文萝卜")
- .build());
- // 更新手机号码:Data.RAW_CONTACT_ID 获取上一条语句插入联系人时产生的 ID
- ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
- .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
- .withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
- .withValue(Phone.NUMBER, "15101689231") // "data1"
- .withValue(Phone.TYPE, Phone.TYPE_MOBILE)
- .withValue(Phone.LABEL, "手机号")
- .build());
- ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
- .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
- .withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
- .withValue(Email.DATA, "[email protected]")
- .withValue(Email.TYPE, Email.TYPE_WORK)
- .build());
-
- // 批量插入 -- 在同一个事务当中
- ContentProviderResult[] results = this.getContext().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
- for(ContentProviderResult result : results){
- Log.i(TAG, result.uri.toString());
- }
- }
- }
普通浏览
复制代码
保存代码
打印代码
-
package wjh.android.contact
;
-
import java.util.ArrayList
;
-
import android.content.ContentProviderOperation
;
-
import android.content.ContentProviderResult
;
-
import android.content.ContentResolver
;
-
import android.content.ContentUris
;
-
import android.content.ContentValues
;
-
import android.database.Cursor
;
-
import android.net.Uri
;
-
import android.provider.ContactsContract
;
-
import android.provider.ContactsContract.RawContacts
;
-
import android.provider.ContactsContract.CommonDataKinds.Email
;
-
import android.provider.ContactsContract.CommonDataKinds.Phone
;
-
import android.provider.ContactsContract.CommonDataKinds.StructuredName
;
-
import android.provider.ContactsContract.Contacts.
Data
;
-
import android.test.AndroidTestCase
;
-
import android.util.Log
;
-
-
-
-
-
public
class ContactTest extends AndroidTestCase {
-
private
static final String TAG =
"ContactTest"
;
-
-
-
-
public
void testGetContacts
(
) throws Throwable {
-
ContentResolver contentResolver =
this.getContext
(
).getContentResolver
(
)
;
-
String uriStr =
"content://com.android.contacts/contacts"
;
-
Uri uri = Uri.parse
(uriStr
)
;
-
Cursor cursor = contentResolver.query
(uri,
null,
null,
null,
null
)
;
-
-
while
(cursor.moveToNext
(
)
) {
-
-
int contactId = cursor.getInt
(cursor.getColumnIndex
(ContactsContract.Contacts._ID
)
)
;
-
-
String name = cursor.getString
(cursor.getColumnIndex
(ContactsContract.Contacts.DISPLAY_NAME
)
)
;
-
-
-
Cursor phones = getContext
(
).getContentResolver
(
).query
(
-
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
-
null,
-
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +
" = "
-
+ contactId,
null,
null
)
;
-
String phone =
""
;
-
while
(phones.moveToNext
(
)
) {
-
-
phone = phones.getString
(phones.getColumnIndex
(ContactsContract.CommonDataKinds.Phone.NUMBER
)
)
;
-
}
-
-
-
Cursor emails = getContext
(
).getContentResolver
(
).query
(
-
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
-
null,
-
ContactsContract.CommonDataKinds.Email.CONTACT_ID +
" = "
-
+ contactId,
null,
null
)
;
-
while
(emails.moveToNext
(
)
) {
-
-
-
-
String emailAddress = emails.getString
(emails.getColumnIndex
(ContactsContract.CommonDataKinds.Email.DATA
)
)
;
-
Log.i
(
"RongActivity",
"emailAddress=" + emailAddress
)
;
-
}
-
emails.close
(
)
;
-
Log.i
(TAG,
"Contact [contactId= "+ contactId +
"name=" + name +
", phone=" + phone +
"]"
)
;
-
}
-
}
-
-
-
-
-
-
public
void testInsert
(
) {
-
ContentValues values =
new ContentValues
(
)
;
-
-
Uri rawContactUri =
this.getContext
(
).getContentResolver
(
).insert
(RawContacts.CONTENT_URI, values
)
;
-
-
-
long rawContactId = ContentUris.parseId
(rawContactUri
)
;
-
-
-
-
-
-
-
values.clear
(
)
;
-
-
-
values.put
(
Data.RAW_CONTACT_ID, rawContactId
)
;
-
-
-
values.put
(
Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE
)
;
-
-
values.put
(StructuredName.GIVEN_NAME,
"文白菜"
)
;
-
this.getContext
(
).getContentResolver
(
).insert
(android.provider.ContactsContract.
Data.CONTENT_URI, values
)
;
-
-
-
values.clear
(
)
;
-
values.put
(
Data.RAW_CONTACT_ID, rawContactId
)
;
-
-
values.put
(
Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE
)
;
-
values.put
(Phone.NUMBER,
"15101689230"
)
;
-
values.put
(Phone.TYPE, Phone.TYPE_MOBILE
)
;
-
this.getContext
(
).getContentResolver
(
).insert
(android.provider.ContactsContract.
Data.CONTENT_URI, values
)
;
-
-
-
values.clear
(
)
;
-
values.put
(
Data.RAW_CONTACT_ID, rawContactId
)
;
-
values.put
(
Data.MIMETYPE, Email.CONTENT_ITEM_TYPE
)
;
-
-
values.put
(Email.TYPE, Email.TYPE_WORK
)
;
-
this.getContext
(
).getContentResolver
(
).insert
(android.provider.ContactsContract.
Data.CONTENT_URI, values
)
;
-
}
-
-
-
-
public
void testSave
(
) throws Throwable{
-
-
ArrayList<ContentProviderOperation> ops =
new ArrayList<ContentProviderOperation>
(
)
;
-
-
int rawContactInsertIndex = ops.size
(
)
;
-
ops.add
(ContentProviderOperation.newInsert
(RawContacts.CONTENT_URI
)
-
.withValue
(RawContacts.ACCOUNT_TYPE,
null
)
-
.withValue
(RawContacts.ACCOUNT_NAME,
null
)
-
.build
(
)
)
;
-
-
ops.add
(ContentProviderOperation.newInsert
(android.provider.ContactsContract.
Data.CONTENT_URI
)
-
.withValueBackReference
(
Data.RAW_CONTACT_ID, rawContactInsertIndex
)
-
.withValue
(
Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE
)
-
.withValue
(StructuredName.GIVEN_NAME,
"文萝卜"
)
-
.build
(
)
)
;
-
-
ops.add
(ContentProviderOperation.newInsert
(android.provider.ContactsContract.
Data.CONTENT_URI
)
-
.withValueBackReference
(
Data.RAW_CONTACT_ID, rawContactInsertIndex
)
-
.withValue
(
Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE
)
-
.withValue
(Phone.NUMBER,
"15101689231"
)
-
.withValue
(Phone.TYPE, Phone.TYPE_MOBILE
)
-
.withValue
(Phone.LABEL,
"手机号"
)
-
.build
(
)
)
;
-
ops.add
(ContentProviderOperation.newInsert
(android.provider.ContactsContract.
Data.CONTENT_URI
)
-
.withValueBackReference
(
Data.RAW_CONTACT_ID, rawContactInsertIndex
)
-
.withValue
(
Data.MIMETYPE, Email.CONTENT_ITEM_TYPE
)
-
-
.withValue
(Email.TYPE, Email.TYPE_WORK
)
-
.build
(
)
)
;
-
-
-
ContentProviderResult
[
] results =
this.getContext
(
).getContentResolver
(
).applyBatch
(ContactsContract.AUTHORITY, ops
)
;
-
for
(ContentProviderResult result : results
){
-
Log.i
(TAG, result.uri.toString
(
)
)
;
-
}
-
}
-
}