(一)、SQLiteDatabase和SQLiteOpenHelper的封装:
(二)、ListView分页显示数据:
目标:能完成以下功能——访问本地网址,实现异步加载数据,解析json数据,分页显示在ListView组件中。
二、ContentProvider简介:
(一)、引入:
数据库在Android当中是私有的,不能将数据库设为WORLD_READABLE,每个数据库都只能创建它的包访问。这意味着只有创建这个数据库的应用程序才可访问它。也就是说不能跨越进程和包的边界,直接访问别的应用程序的数据库。那么如何在应用程序间交换数据呢? 如果需要在进程间传递数据,可以使用ContentProvider来实现。
(二)、ContentProvider的功能和意义:
为了在应用程序之间交换数据,Android提供了ContentProvider,ContentProvider是不同应用程序之间进行数据交换的标准API。当一个应用程序需要把自己的数据暴露给其他应用程序使用时,该应用程序可以通过提供ContentProvider来实现;而其他应用程序需要使用这些数据时,可以通过ContentResolver来操作ContentProvider暴露的数据。
一旦某个应用程序通过ContentProvider暴露了自己的数据操作接口,那么不管该应用程序是否启动,其他应用程序都可以通过该接口来操作被暴露的内部数据,包括增加数据、删除数据、修改数据、查询数据等。
虽然大部分使用ContentProvider操作的数据都来自于数据库,但是也可以来自于文件、SharedPreferences、XML或网络等其他存储方式。
(三)、核心类:
1、ContentProvider:(A应用暴露数据)
- 一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据暴露出去;
- 外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以修改程序的数据。
2、ContentResolver:(操作A应用所暴露的数据)
- 外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据;
- ContentResolver 可以理解成是HttpClient的作用。
3、 Uri:Uri是ContentResolver和ContentProvider进行数据交换的标识。
- 每个ContentProvider提供公共的URI来唯一标识其数据集。管理多个数据集的(多个表)的 ContentProvider 为每个数据集提供了单独的URI。
- Uri 的标准前缀:以“content://”作为前缀,这个是标准的前缀,表示该数据由 ContentProvider 管理。
- Uri 的authority部分:该部分是完整的类名。(使用小写形式)。
- Uri 的path部分(资源部分、数据部分): 用于决定哪类数据被请求。
- 被请求的特定记录的id值。如果请求不仅限于某个单条数据,该部分及其前面的斜线应该删除。
- 为了将一个字符串转换成Uri,Android中提供了Uri的parse()静态方法来实现。
【备注:】URI、URL、URN的区别:
- 首先,URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源。
- URL是uniform resource locator,统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。
- URN,uniform resource name,统一资源命名,是通过名字来标识资源,比如mailto:java-net@java.sun.com。
也就是说,URI是以一种抽象的,高层次概念定义统一资源标识,而URL和URN则是具体的资源标识的方式。URL和URN都是一种URI。
总结一下:URL是一种具体的URI,它不仅唯一标识资源,而且还提供了定位该资源的信息。URI是一种语义上的抽象概念,可以是绝对的,也可以是相对的,而URL则必须提供足够的信息来定位,所以,是绝对的。
(四)、系统内置的预定义ContentProvider:
1、Contacts:获取、修改、保存联系人的信息;
2、MediaStore:访问声音、视频、图片等多媒体文件;
3、CallLog :查看或更新通话记录;
4、Browser:读取或修改浏览历史、网络搜索、书签;
5、Setting:查看和获取蓝牙设置、铃声设置等设备首选项。
6、SMS:短信
三、使用ContentResolver 管理联系人:
(一)、 使用ContentResolver 操作数据的步骤:
1、调用Context的getContentResolver()方法获得ContentResolver 对象;
2、调用使用ContentResolver 的insert()、delete()、update()、query()方法操作数据。
- Uri insert(Uri uri, ContentValues values)
- int delete(Uri uri, String where, String[] whereArgs)
- int update(Uri uri, ContentValues values, String where, String[] whereArgs)
- Cursor query(Uri uri, String[] projection, String where, String[] whereArgs, String sortOrder)
参数解释:
String where:表示带有占位符的where子句组成的字符串;
String[] whereArgs:表示替换where参数中占位符后的数据组成的字符串数组;
String sortOrder:表示select语句中的order by子句组成的字符串;
String[] projection:表示select语句中需要查询的所有的字段组成的字符串数组。
ContentValues values:是由数据库中表字段和往该字段中放置的数据所组成的键值对对象。
【备注:】以上四个方法的参数分别是2、3、4、5个。
(二)、 联系人管理中ContentProvider的几个Uri:
1、联系人的Uri==> content://com.android.contacts/contacts 和 content://com.android.contacts/raw_contacts
2、电话号码的Uri==> content://com.android.contacts/data/phones
3、EMAIL的URI==> content://com.android.contacts/data/emails
不过为了方便记忆,系统中提供了以下常量来替代以上的Uri字符串。
- Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI;
- Uri PHONE_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
- Uri EMAIL_URI = ContactsContract.CommonDataKinds.Email.CONTENT_URI;
(三)、示例代码1——查看通讯录中的联系人姓名、id、电话、Email等信息:
publicstatic List<Map<String, Object>> selectContactsMsg(
ContentResolver resolver, ContentValues values) {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
String uri_contacts = "content://com.android.contacts/raw_contacts";
String uri_contacts_phones = "content://com.android.contacts/data/phones";
String uri_contacts_emails = "content://com.android.contacts/data/emails";
// 从raw_contacts表中或许联系人的id和联系人的姓名。
Cursor cursor_contacts = resolver.query(Uri.parse(uri_contacts),
new String[] { "_id", "display_name" }, null, null, null);
// 遍历所有的联系人的信息
while (cursor_contacts.moveToNext()) {
int contacts_id = cursor_contacts.getInt(cursor_contacts
.getColumnIndex("_id"));
String display_name = cursor_contacts.getString(cursor_contacts
.getColumnIndex("display_name"));
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", contacts_id);
map.put("display_name", display_name);
// 以下开始获取电话号码
// 根据每个联系人的id再去data表中查找相应的电话号码。
Cursor cursor_phones = resolver.query(
Uri.parse(uri_contacts_phones), new String[] {
"raw_contact_id", "data1" }, "raw_contact_id=?",
new String[] { contacts_id + "" }, null);
// 因为电话号码可能是多个,所以需要再遍历,组合在一起形成一个电话号码的字符串,放到StringBuilder中
StringBuilder sb = new StringBuilder();
while (cursor_phones.moveToNext()) {
sb.append(cursor_phones.getString(1));
sb.append(" | ");
}
// 将生成的电话号码放到map集合中
map.put("phones", sb.toString());
// 以下开始或许Email信息
Cursor cursor_emails = resolver.query(
Uri.parse(uri_contacts_emails), new String[] {
"raw_contact_id", "data1" }, "raw_contact_id=?",
new String[] { contacts_id + "" }, null);
StringBuilder sb2 = new StringBuilder();
while (cursor_emails.moveToNext()) {
sb2.append(cursor_emails.getString(1));
sb2.append(" | ");
}
map.put("emails", sb2.toString());
// 将包含有id、联系人姓名、手机号码、emails的map放到list集合中
list.add(map);
}
return list;
}
(四)、示例代码2——实现新添加通讯录信息:
// 以下方法是利用常量值所写的插入联系人的方法。如果希望封装成方法,再增加一个Map集合的参数,用来传入信息即可。
publicvoid insertContacts(ContentValues values, ContentResolver resolver) {
// 首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
Uri rawContactUri = resolver.insert(
ContactsContract.RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
// 往data表入姓名数据
values.clear();
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
values.put(
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
values.put(
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
"王向军");
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
"王向军");
resolver.insert(ContactsContract.Data.CONTENT_URI, values);
// 往data表入电话数据
values.clear();
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, "13520551441");
values.put(ContactsContract.CommonDataKinds.Phone.TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
resolver.insert(ContactsContract.Data.CONTENT_URI, values);
// 往data表入Email数据
values.clear();
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.Email.DATA,
"237235475@qq.com");
values.put(ContactsContract.CommonDataKinds.Email.TYPE,
ContactsContract.CommonDataKinds.Email.TYPE_WORK);
resolver.insert(ContactsContract.Data.CONTENT_URI, values);
}
(五)、封装ContentResolver操作系统联系人的工具类ContactsResolverHelper:【重要】
publicclass ContactsResolverHelper {
/*
* / 以下代码是插入新联系人的子过程。其中第三个参数:List集合表示要插入到通讯录中的新数据(该集合中有三个值,分别是:姓名、电话、email)。
*/
publicvoid insertContacts(ContentResolver resolver, ContentValues values,
List<String> list) {
String uri_rawcontacts = "content://com.android.contacts/raw_contacts";
String uri_data = "content://com.android.contacts/data";
Uri uri = Uri.parse(uri_contacts);
// 首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
Uri rawContactUri = resolver.insert(Uri.parse(uri_rawcontacts), values);
long contact_id = ContentUris.parseId(rawContactUri);
// 往data表中插入一条用户的名称信息
values.put("raw_contact_id", contact_id);
values.put("mimetype", "vnd.android.cursor.item/name");
values.put("data1", list.get(0));
values.put("data2", list.get(0));
resolver.insert(uri_data, values);
// 往data表中插入电话信息
values.clear();
values.put("raw_contact_id", contact_id);
values.put("mimetype", "vnd.android.cursor.item/phone_v2");
values.put("data1", list.get(1));
values.put("data2", 2);// 2,Phone.TYPE_MOBILE ,表示手机号码
resolver.insert(uri_data, values);
// 往data表中插入Email信息
values.clear();
values.put("raw_contact_id", contact_id);
values.put("mimetype", "vnd.android.cursor.item/email_v2");
values.put("data1", list.get(2));
values.put("data2", 2);// 2,Email.TYPE_WORK , 表示工作用Email号码
resolver.insert(uri_data, values);
}
// 以下代码是删除联系人信息的方法。
publicstaticint deleteContacts(ContentResolver resolver, String where,
String[] whereArgs) {
String uri_contacts = "content://com.android.contacts/raw_contacts";
return resolver.delete(Uri.parse(uri_contacts), where, whereArgs);
}
// 以下代码是查询联系人信息的方法。
publicstatic Cursor selectContactsName(ContentResolver resolver,
String[] projection, String where, String[] whereArgs,
String sortOrder) {
String uri_contacts = "content://com.android.contacts/raw_contacts";
return resolver.query(Uri.parse(uri_contacts), projection, where,
whereArgs, sortOrder);
}
publicstatic List<Map<String, Object>> selectContactsMsg(
ContentResolver resolver, ContentValues values) {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
String uri_contacts = "content://com.android.contacts/raw_contacts";
String uri_contacts_phones = "content://com.android.contacts/data/phones";
String uri_contacts_emails = "content://com.android.contacts/data/emails";
// 从raw_contacts表中或许联系人的id和联系人的姓名。
Cursor cursor_contacts = resolver.query(Uri.parse(uri_contacts),
new String[] { "_id", "display_name" }, null, null, null);
// 遍历所有的联系人的信息
while (cursor_contacts.moveToNext()) {
int contacts_id = cursor_contacts.getInt(cursor_contacts
.getColumnIndex("_id"));
String display_name = cursor_contacts.getString(cursor_contacts
.getColumnIndex("display_name"));
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", contacts_id);
map.put("display_name", display_name);
// 以下开始获取电话号码
// 根据每个联系人的id再去data表中查找相应的电话号码。
Cursor cursor_phones = resolver.query(
Uri.parse(uri_contacts_phones), new String[] {
"raw_contact_id", "data1" }, "raw_contact_id=?",
new String[] { contacts_id + "" }, null);
// 因为电话号码可能是多个,所以需要再遍历,组合在一起形成一个电话号码的字符串,放到StringBuilder中
StringBuilder sb = new StringBuilder();
while (cursor_phones.moveToNext()) {
sb.append(cursor_phones.getString(1));
sb.append(" | ");
}
// 将生成的电话号码放到map集合中
map.put("phones", sb.toString());
// 以下开始或许Email信息
Cursor cursor_emails = resolver.query(
Uri.parse(uri_contacts_emails), new String[] {
"raw_contact_id", "data1" }, "raw_contact_id=?",
new String[] { contacts_id + "" }, null);
StringBuilder sb2 = new StringBuilder();
while (cursor_emails.moveToNext()) {
sb2.append(cursor_emails.getString(1));
sb2.append(" | ");
}
map.put("emails", sb2.toString());
// 将包含有id、联系人姓名、手机号码、emails的map放到list集合中
list.add(map);
}
return list;
}
// 以下代码是更新联系人姓名的方法。
publicstaticint updateContactsName(ContentValues values,
ContentResolver resolver, String oldName, String newName) {
String uri_contacts = "content://com.android.contacts/raw_contacts";
String uri_contacts_data = "content://com.android.contacts/data";
// 先更新raw_contacts表中的联系人姓名,共有四列需要更新
values.put("display_name", newName);
values.put("display_name_alt", newName);
values.put("sort_key", newName);
values.put("sort_key_alt", newName);
int count1 = resolver.update(Uri.parse(uri_contacts), values,
"display_name=?", new String[] { oldName });
// 再更新data表中联系人的姓名,共有两列
values.clear();
values.put("data1", newName);
values.put("data2", newName);
int count2 = resolver.update(Uri.parse(uri_contacts_data), values,
"data1=? and mimetype_id=?", new String[] { oldName, "7" });
// 返回2,则说明六项都被更新。如果只返回1,则说明没有修改完全
return count1 + count2;
}
}
四、系统自带ContentProvider的常用Uri地址:
(一)、Android系统管理联系人的Uri如下:
- ContactsContract.Contacts.CONTENT_URI 管理联系人的Uri
- ContactsContract.CommonDataKinds.Phone.CONTENT_URI 管理联系人的电话的Uri
- ContactsContract.CommonDataKinds.Email.CONTENT_URI 管理联系人的Email的Uri
【数据库中主要字段:】
- 联系人id字段名称为:ContactsContract.Contacts._ID
- 联系人name 字段为:ContactContract.Contracts.DISPLAY_NAME
- 电话信息表的外键id为:ContactsContract.CommonDataKinds.Phone.CONTACT_ID
- 电话号码 字段为:ContactsContract.CommonDataKinds.Phone.NUMBER.
- Email 字段为:ContactsContract.CommonDataKinds.Email.DATA
- 其外键为:ContactsContract.CommonDataKinds.Email.CONTACT_ID
(二)、Android为多媒体提供的ContentProvider的Uri如下:
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI 存储在SD卡上的音频文件
- MediaStore.Video.Media.EXTERNAL_CONTENT_URI 存储在 SD卡上的视频
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI 存储在 SD卡上的图片文件内容
- MediaStore.Audio.Media.INTERNAL_CONTENT_URI 手机内部存储器上的音频文件
- MediaStore.Video.Media.INTERNAL_CONTENT_URI 手机内部存储器上的视频
- MediaStore.Images.Media.INTERNAL_CONTENT_URI 手机内部存储器上的图片
图片数据库字段:20个
_id ,_data,_size,_display_name,mime_type,title,date_added,date_modified,description,picasa_id,isprivate,latitude,longitude,datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name,width,height
音频数据字段:29个
_id,_data,_display_name,_size,mime_type,date_added,is_drm,date_modified,title,title_key,duration,artist_id,composer,album_id,track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast,bookmark,album_artist,artist_id:1,artist_key,artist,album_id:1,album_key,album,
视频数据字段:27个
_id,_data,_display_name,_size,mime_type,date_added,date_modified,title,duration,artist,album,resolution,description,isprivate,tags,category,language,mini_thumb_data,latitude,longitude,datetaken,mini_thumb_magic,bucket_id,bucket_display_name,bookmark,width,height,
【数据库中主要字段:】
- 图片名称字段:Media.DISPLAY_NAME
- 图片的详细描述字段:Media.DESCRIPTION
- 图片的保存位置字段:Media.DATA
(三)、短信Uri:
- content://sms 所有短信
- content://sms/outbox 发送箱中的短信
- content://sms/inbox 收件箱中短信
【数据库中主要字段:】
- 短信手机号码 : address
- 短信标题: subject
- 短信内容:body
- 短信发送时间戳:date
- 短信类型:type 【0:待发信息; 1:接收到信息; 2:发出】
- _id
(四)、通话记录Uri:
- content://call_log/calls 所有通话记录 "_id", "number", "date", "type"
1、完善代码“ContentResolver管理联系人”,增加添加联系人功能,增加删除联系人功能。
2、利用ContentResolver制作管理SDCard上的多媒体文件管理器。
3、利用ContentResolver+AutoCompleteTextView实现自动补全联系人姓名。