五、ContentProvider 内容提供者
续上一博文(Android核心基础-5.Android 数据存储与访问-4.ContentProvider 内容提供者-示例(监听短信)
前面讲了内容提供者ContentProvider和监听数据修改的ContentObserver
本讲续上一博文“短信的监听”讲解“操作联系人”
9.1 联系人
- 读联系人
- 联系人数据是存储在com.android.providers.contacts应用的数据库中的, 该应用对外提供了ContentProvider
- 下载原码可以得到Uri和path
- 查询raw_contacts表得到手机中联系人的id, 用id作为条件查询data表即可得到联系人的数据
- 写联系人
- 先向raw_contacts表写一个id
- 再用这个id向data表写联系人数据
- 批量操作
- ContentResolver.applyBatch()
9.2 探索路
我们启动模拟器->打开短信。发现log打印了一行如下:
07-26 15:21:47.882: I/ActivityManager(1007): START {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.android.contacts/.activities.PeopleActivity bnds=[64,417][128,481] u=0} from pid 1146
从日志中我们看到系统启动的联系人应用程序的包名是com.android.contacts
根据这个包名我们去系统目录下查找data/data/com.android.contacts
很遗憾的发现没有数据库文件,那么我们添加的联系人数据都储存在哪里呢?
据我们上一“监听短信”博文中的学习知识可以知道,其也是一个展示界面,数据存储在另一个程序中,其通过ContentProvider对数据进行操作。
数据存储的程序为:com.android.providers.contacts
找到其程序目录下的库contacts2
该库中有好多表,但是我们关心表就三个。其他表我们现阶段不探讨
9.3 contacts2库的表关系
raw_contacts我们值关心_id字段他会外键关联到data表
data 中的raw_contacts_id对应raw_contacts表中的_id
mimetypes表
_id | mimetype |
---|---|
1 | vnd.android.cursor.item/email_v2 |
2 | vnd.android.cursor.item/im |
3 | vnd.android.cursor.item/nickname |
4 | vnd.android.cursor.item/organization |
5 | vnd.android.cursor.item/phone_v2 |
6 | vnd.android.cursor.item/sip_address |
7 | vnd.android.cursor.item/name |
8 | vnd.android.cursor.item/postal-address_v2 |
9 | vnd.android.cursor.item/identity |
10 | vnd.android.cursor.item/photo |
11 | vnd.android.cursor.item/group_membership |
分析:
我们根据raw_contacts表_id的数量就知道有多少联系人
根据该_id查询data表raw_contacts_id字段就获得了每个联系人的数据
再根据每条联系人数据的mimetype_id字段对应mimetypes表的关系,就知道每条数据对应的数据类型。
9.4 Uri的获取
我们找到他的源码;
到github搜索到地址:https://github.com/CyanogenMod/android_packages_providers_ContactsProvider
找到联系人源码。
清单文件:
<provider android:name="ContactsProvider2"
android:authorities="contacts;com.android.contacts"//授权/主机名(前面那个是早版本的,我们使用后面的)
android:label="@string/provider_label"//标签
android:multiprocess="false"//是否允许多进程同时使用
android:exported="true"//应用程序可以被其他应用使用
android:readPermission="android.permission.READ_CONTACTS"//读权限
android:writePermission="android.permission.WRITE_CONTACTS">//写权限
<path-permission
android:pathPrefix="/search_suggest_query"
android:readPermission="android.permission.GLOBAL_SEARCH" />
<path-permission
android:pathPrefix="/search_suggest_shortcut"//操作其子集路径的权限
android:readPermission="android.permission.GLOBAL_SEARCH" />
<path-permission
android:pathPattern="/contacts/.*/photo"
android:readPermission="android.permission.GLOBAL_SEARCH" />
<grant-uri-permission android:pathPattern=".*" />
</provider>
9.5 读取联系人
清单文件中配置读取联系人权限:
< uses-permission android:name=”android.permission.READ_CONTACTS”/>
读取所有联系人
private Uri rawContactsUri = Uri.parse("content://com.android.contacts/raw_contacts");
private Uri dataUri = Uri.parse("content://com.android.contacts/data");
public void testRead() {
ContentResolver resolver = getContext().getContentResolver();
// 先查raw_contacts表, 得到所有id(contact_id非null)
Cursor rawContactsCursor = resolver.query(rawContactsUri, new String[] { "_id" }, null, null, null);
while (rawContactsCursor.moveToNext()) {
long id = rawContactsCursor.getLong(0);
// 用id作为条件, 查询data表中的数据
Cursor dataCursor = resolver.query(dataUri, new String[] { "mimetype", "data1" }, "raw_contact_id=?", new String[] { id + "" }, null);
while (dataCursor.moveToNext()) {
String mimetype = dataCursor.getString(0);
String data = dataCursor.getString(1);
System.out.println(mimetype + ": " + data);
}
}
}
运行程序打印如下:
07-26 17:17:08.497: I/System.out(4748): vnd.android.cursor.item/phone_v2: 1 501-099-0415
07-26 17:17:08.497: I/System.out(4748): vnd.android.cursor.item/email_v2: 374452668@qq.com
07-26 17:17:08.497: I/System.out(4748): vnd.android.cursor.item/name: 李建
07-26 17:17:08.497: I/System.out(4748): vnd.android.cursor.item/phone_v2: 555-6
07-26 17:17:08.497: I/System.out(4748): vnd.android.cursor.item/name: 张三丰
07-26 17:17:08.497: I/System.out(4748): vnd.android.cursor.item/phone_v2: 555-8
07-26 17:17:08.497: I/System.out(4748): vnd.android.cursor.item/name: 猪八戒
9.6 写入联系人
清单文件中配置读取联系人权限:
< uses-permission android:name=”android.permission.WRITE_CONTACTS”/>
写入单条联系人
public void testWrite() {
ContentResolver resolver = getContext().getContentResolver();
ContentValues values = new ContentValues();
// 先向raw_contacts表写一个id
Uri resultUri = resolver.insert(rawContactsUri, values);
long id = ContentUris.parseId(resultUri);
// 用这个id作为data表的raw_contact_id, 再写3条数据
values.put("raw_contact_id", id);
values.put("mimetype", "vnd.android.cursor.item/name");
values.put("data1", "刘德华");
resolver.insert(dataUri, values);
values.put("mimetype", "vnd.android.cursor.item/phone_v2");
values.put("data1", "13012345678");
resolver.insert(dataUri, values);
values.put("mimetype", "vnd.android.cursor.item/email_v2");
values.put("data1", "ldh@163.com");
resolver.insert(dataUri, values);
}
批量插入多条联系人
// Android系统给我们封装好了事务,要么全部成功,要么全部失败
public void testWriteBatch() throws Exception {
ContentResolver resolver = getContext().getContentResolver();
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); // 创建集合, 用来装多个操作
ContentProviderOperation operation1 = ContentProviderOperation.newInsert(rawContactsUri) // 创建一个Insert操作的Builder
.withValue("_id", null) // 设置要插入列名和数据(列名为_id, 数据为null时自动生成)
.build(); // 生成插入操作对象
ContentProviderOperation operation2 = ContentProviderOperation.newInsert(dataUri)
.withValueBackReference("raw_contact_id", 0) // 插入的数据是同组第1个操作的结果
.withValue("mimetype", "vnd.android.cursor.item/name")
.withValue("data1", "齐天大圣")
.build();
ContentProviderOperation operation3 = ContentProviderOperation.newInsert(dataUri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/phone_v2")
.withValue("data1", "13187654321")
.build();
ContentProviderOperation operation4 = ContentProviderOperation.newInsert(dataUri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/email_v2")
.withValue("data1", "qtds@163.com")
.build();
operations.add(operation1);
operations.add(operation2);
operations.add(operation3);
operations.add(operation4);
resolver.applyBatch("com.android.contacts", operations); // 一次性执行集合中的多个操作
}
示例源代码->百度网盘