读文档读到Content Provider一节,文档中提供了获取通讯录的数据的方法,所以实践了一下,发现了很多问题。
Content Provider存储数据的模型是基于一张表格的,其实也就是数据库,每一个Content Provider都提供一个URI来标识这个data set模型,这个URI提供一个以"content://"开头的字符串,来表明此data set是在哪个Content Provider控制之下的,这个URI在类中通常定义为public static final CONTENT_URI = "content://xxxxxx ",在实现自己的Content Provider的时候,通常也定义自己的CONTENT_URI.比如说在sdk文档中的notepad例子中的ContentProvider中就定义了自己的Content URI标识: public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/notes");
文档中提到,电话本的数据模型可能是被定义为这样子的:
_ID | NUMBER | NUMBER_KEY | LABEL | NAME | TYPE |
---|---|---|---|---|---|
13 | (425) 555 6677 | 425 555 6677 | Kirkland office | Bully Pulpit | TYPE_WORK |
44 | (212) 555-1234 | 212 555 1234 | NY apartment | Alan Vain | TYPE_HOME |
45 | (212) 555-6657 | 212 555 6657 | Downtown office | Alan Vain | TYPE_MOBILE |
53 | 201.555.4433 | 201 555 4433 | Love Nest | Rex Cars | TYPE_HOME |
其中每一条记录都有一个_ID唯一的标识,这个ID可以在不同的数据库表格中起关联作用。
一般,我们访问Content Provider的时候不是直接访问,而是通过ContentResolver来方式,contentResolver可以通过getContentResolver()得到,一般resolver的query返回一个Cursor对象,这个Cursor对象就是操作遍历上述表格的工具了,他可以从行到行,也可以从列到列,从而访问到其中的数据,在使用实例的时候大家会体会到其作用。
说一下文档中提供的通过Content Resolver查询电话本的方式,需要注意的是访问隐私信息的app需要先在manifest.xml中声明访问权限,在manifest节点内加入
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
否则会出现SecurityException。
访问代码如下:
import android.provider.Contacts.People;
import android.database.Cursor;
// Form an array specifying which columns to return.
String[] projection = new String[] {
People._ID,
People._COUNT,
People.NAME,
People.NUMBER
};
// Get the base URI for the People table in the Contacts content provider.
Uri contacts = People.CONTENT_URI;
// Make the query.
Cursor managedCursor = managedQuery(contacts,
projection, // Which columns to return
null, // Which rows to return (all rows)
null, // Selection arguments (none)
// Put the results in ascending order by name
People.NAME + " ASC");
然后,通过返回的Cursor来访问数据就可以了,代码:
if (cur.moveToFirst()) {
String name;
String phoneNumber;
int nameColumn = cur.getColumnIndex(People.NAME);
int phoneColumn = cur.getColumnIndex(People.NUMBER);
String imagePath;
do {
// Get the field values
name = cur.getString(nameColumn);
phoneNumber = cur.getString(phoneColumn);
// Do something with the values.
...
} while (cur.moveToNext());
}
这样就取回了数据。
问题是,我们所用的方法中,有很多的方法和类已经被废弃了,比如import android.provider.Contacts.Phones;就已经被废弃,我想做的就是不用这些废弃的类和方法,而是采用其提供的新的方法,通过api的提示,新的类可以参考ContactsContract , 打开这个类我们可以看到,这个类定义了许多contact相关的拓展类,包括ContactsContract.Data,ContactsContract.RawContacts ,
ContactsContract.Contacts
等等相关的类,我们可以知道这个电话本的数据库看一下:
打开
eclipse
,启动模拟器,
window-> show view-> other -> android -> file exploer
打开文件浏览器窗口,定位到
data->data->com.android.providers.contacts->database
文件夹,找到
contact2.db
点击右上方的导出按钮导出此数据库文件
用SQLite
数据库的工具打开
(
比如
SQLite Expert Personal 3)
,可以看到其中的表和视图,注意这里我们的模拟器是
2.1
版本,对比
api
中
contactsContract
类中的拓展类和这里的表格不难发现,其实这些类大致对应数据库的表格,至于为什么电话本的数据库的表格要这样分,而不是像上面的那个表格那么简单可以自己体会,其功能理念不同。
上面的表格中每个表格都有各自的信息分类,电话号码和姓名是不在一个表格中的,这个就给我们的数据获取工作带来了一定的困难,我们可以定位到下面的
view_v1_phones视图
可以看到这个视图就将我们需要的基本的
number
和
name
合并在了一起,获取的话肯定更方便,但是,
api
中我们看不到针对此视图的
Content Provider
,取数据的话只能从两个表格利用查询条件取,可以点击这个视图的
Design
选项卡,读一下其中的
sql
语句发现这个视图也是将几个
table
合并在一起的。可以根据这个思想来用
content Resolver
从两个表格分表读数据然后合并
(
根据
Phone_lookup
和
name_lookup
的
id
与
data
中的
id
相等比较
)
,具体方法应该能体会到。一开始我就用的这种方法,觉得实在没废弃的方法好用。
注意到这个视图带着
v1
的字样,我猜想可能是兼容的旧的数据库设计,又看到
api
文档中的
ContactsContract
类是从
api 5
开始的,所以启动
1.6
的模拟器,再次导出这个数据库文件
contacts.db,
注意,这里就没有
2
的字样了,打开这个数据库果然发现,其实
2
版本的数据库中的
view
其实是
1
版本中的数据库的一个兼容而已,旧的类和方法在新版本中大概也就是操作这些
view
来完成的。
纠结良久,我还是决定用被废弃的
api
。。。