第11章 ContentProvider实现数据共享

tb教学录像:42分钟)

Content Provider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为在Android中没有提供所有应用共同访问的公共存储区域。本章将介绍如何使用预定义和自定义Content Provider。

通过阅读本章,您可以:

★ 了解Content Provider的基本概念

★ 掌握Content Provider的常用方法

★ 了解系统预定义的Content Provider

★ 了解如何自定义Content Provider

11.1 Content Provider概述

tb教学录像:光盘\TM\lx\11\ Content Provider概述.exe

Content Provider内部如何保存数据由其设计者决定,但是所有的Content Provider都实现一组通用的方法,用来提供数据的增、删、改、查功能。

客户端通常不会直接使用这些方法,大多数是通过ContentResolver对象实现对Content Provider的操作。开发人员可以通过调用Activity或者其他应用程序组件的实现类中的getContentResolver()方法来获得ContentProvider对象,例如:

  1. ContentResolver cr = getContentResolver();

使用ContentResolver提供的方法可以获得Content Provider中任何感兴趣的数据。

当开始查询时,Android系统确认查询的目标Content Provider并确保它正在运行。系统会初始化所有ContentProvider类的对象,开发人员不必完成此类操作,实际上,开发人员根本不会直接使用ContentProvider类的对象。通常,每个类型的ContentProvider仅有一个单独的实例。但是该实例能与位于不同应用程序和进程的多个ContentResolver类对象通信。不同进程之间的通信由ContentProvider类和ContentResolver类处理。

11.1.1 数据模型

Content Provider使用基于数据库模型的简单表格来提供其中的数据,这里每行代表一条记录,每列代表特定类型和含义的数据。例如,联系人的信息可能以如表11.1所示的方式提供。

表11.1 联系方式

_ID NAME NUMBER EMAIL
001 张×× 123* 123@163.com
002 王×× 132* 132@google.com
003 李×× 312* 312@qq.com
004 赵×× 321* 321**@126.com

每条记录包含一个数值型的_ID字段,用于在表格中唯一标识该记录。ID能用于匹配相关表格中的记录,例如,在一个表格中查询联系人的电话,在另一表格中查询其照片。

注意:ID字段前还包含了一条下划线,在编写代码时不要忘记。

查询返回一个Cursor对象,它能遍历各行各列来读取各个字段的值。对于各个类型的数据,它都提供了专用的方法。因此,为了读取字段的数据,开发人员必须知道当前字段包含的数据类型。

11.1.2 URI的用法

每个Content Provider提供公共的URI(使用Uri类包装)来唯一标识其数据集。管理多个数据集(多个表格)的Content Provider为每个数据集提供了单独的URI。所有为provider提供的URI都以“content://”作为前缀,“content://”模式表示数据由Content Provider来管理。

如果自定义Content Provider,则应该为其URI也定义一个常量,来简化客户端代码并让日后更新更加简洁。Android为当前平台提供的Content Provider定义了CONTENT_URI常量。例如,匹配电话号码到联系人表格的URI和匹配保存联系人照片表格的URI分别如下:

  1. android.provider.Contacts.Phones.CONTENT_URI
  2. android.provider.Contacts.Photos.CONTENT_URI

URI常量用于所有与Content Provider的交互中。每个ContentResolver方法使用URI作为其第一个参数。它标识ContentResolver应该使用哪个provider及其中的哪个表格。

下面是Content URI重要部分的总结:

371-1

[√]A:标准的前缀,用于标识该数据由Content Provider管理,不需修改。

[√]B:URI的authority部分,用于标识该Content Provider。对于第三方应用,该部分应该是完整的类名(使用小写形式)来保证唯一性。在<provider>元素的authorities属性中声明authority。

[√]C:Content Provider的路径部分,用于决定哪类数据被请求。如果Content Provider仅提供一种数据类型,可以省略该部分;如果provider提供几种类型,包括子类型,这部分可以由几部分组成。

[√]D:被请求的特定记录的ID值。这是被请求记录的_ID值。如果请求不仅限于单条记录,该部分及其前面的斜线应该删除。

11.2 预定义Content Provider

tb教学录像:光盘\TM\lx\11\ 预定义Content Provider.exe

Android系统为常用数据类型提供了很多预定义的Content Provider(声音、视频、图片、联系人等),它们大多位于android.provider包中。开发人员可以查询这些provider以获得其中包含的信息(尽管有些需要适当的权限来读取数据)。Android系统提供的常见Content Provider说明如下。

[√]Browser:读取或修改书签、浏览历史或网络搜索。

[√]CallLog:查看或更新通话历史。

[√]Contacts:获取、修改或保存联系人信息。

[√]LiveFolders:由Content Provider提供内容的特定文件夹。

[√]MediaStore:访问声音、视频和图片。

[√]Setting:查看和获取蓝牙设置、铃声和其他设备偏好。

[√]SyncStateContract:用于使用数据数组账号关联数据的ContentProvider约束。希望使用标准方式保存数据的provider时可以使用。

[√]UserDictionary:在可预测文本输入时,提供用户定义单词给输入法使用。应用程序和输入法能增加数据到该字典。单词能关联频率信息和本地化信息。

11.2.1 查询数据

要查询Content Provider中的数据,需要以下3个信息:

[√]标识该Content Provider的URI。

[√]需要查询的数据字段名称。

[√]字段中数据的类型。

如果查询特定的记录,则还需要提供该记录的ID值。

为了查询Content Provider中的数据,开发人员需要使用ContentResolver.query()或Activity.managedQuery()方法。这两个方法使用相同的参数,并且都返回Cursor对象。但是managedQuery()方法导致Activity管理Cursor的生命周期。托管的Cursor处理所有的细节,如当Activity暂停时卸载自身,当Activity重启时加载自身。调用Activity.startManagingCursor()方法可以让Activity管理未托管的Cursor对象。

query()和managedQuery()方法的第一个参数是provider的URI,即标识特定ContentProvider和数据集的CONTENT_URI常量。

为了限制仅返回一条记录,可以在URI结尾增加该记录的_ID值,即将匹配ID值的字符串作为URI路径部分的结尾片段。例如,ID值是10,URI将是:

  1. content://.../10

有些辅助方法,特别是ContentUris.withAppendedId()和Uri.withAppendedPath()方法,能轻松地将ID增加到URI。这两个方法都是静态方法,并返回一个增加了ID的Uri对象。

query()和managedQuery()方法的其他参数用来更加细致地限制查询结果,它们是:

[√]应该返回的数据列名称。null值表示返回全部列;否则,仅返回列出的列。全部预定义Content Provider为其列都定义了常量。例如,android.provider.Contacts.Phones类定义了_ID、NUMBER、NUMBER_KEY、NAME等常量。

[√]决定哪些行被返回的过滤器,格式类似SQL的WHERE语句(但是不包含WHERE自身)。null值表示返回全部行(除非URI限制查询结果为单行记录)。

[√]选择参数。

[√]返回记录的排序器,格式类似SQL的ORDER BY语句(但是不包含ORDER BY自身)。null值表示以默认顺序返回记录,这可能是无序的。

查询返回一组0条或多条数据库记录。列名、默认顺序和数据类型对每个Content Provider都是特别的。但是每个provider都有一个_ID列,它为每条记录保存唯一的数值ID。每个provider也能使用_COUNT报告返回结果中记录的行数,该值在各行都是相同的。

获得数据使用Cursor对象处理,它能向前或向后遍历整个结果集。开发人员可以使用Cursor对象来读取数据,而增加、修改和删除数据则必须使用ContentResolver对象。

11.2.2 增加记录

为了向Content Provider中增加新数据,首先需要在ContentValues对象中建立键值对映射,这里每个键匹配Content Provider中列名,每个值是该列中希望增加的值。然后调用ContentResolver.insert()方法并传递给它provider的URI参数和ContentValues映射。该方法返回新记录的完整URI,即增加了新记录ID的URI。开发人员可以使用该URI来查询并获取该记录的Cursor,以便修改该记录。

11.2.3 增加新值

一旦记录存在,开发人员可以向其中增加新信息或者修改已经存在的信息。增加记录到Contacts数据库的最佳方式是增加保存新数据的表名到代表记录的URI,然后使用组装好的URI来增加新数据。每个Contacts表格以CONTENT_DIRECTORY常量的方式提供名称。

开发人员可以调用使用byte数组作为参数的ContentValues.put()方法向表格中增加少量二进制数据,这适用于类似小图标的图片、短音频片段等。然而,如果需要增加大量二进制数据,如图片或者完整的歌曲等,则需要保存代表数据的content:URI到表格,然后使用文件URI调用ContentResolver. openOutputStream()方法。这导致Content Provider保存数据到文件并在记录的隐藏字段保存文件路径。

11.2.4 批量更新记录

要批量更新数据(例如,将全部字段中“NY”替换成“New York”),可使用ContentResolver.update()方法并提供需要修改的列名和值。

11.2.5 删除记录

如果需要删除单条记录,可调用ContentResolver.delete()方法并提供特定行的URI。

如果需要删除多条记录,可调用ContentResolver.delete()方法并提供删除记录类型的URI(如android.provider.Contacts.People.CONTENT_URI)和一个SQL WHERE语句,它定义哪些行需要删除。

注意:请确保提供了一个合适的WHERE语句,否则可能删除全部数据。

11.2.6 范例1:系统内置联系人的使用

由于本章范例主要使用系统内置联系人来演示Content Provider的使用,下面先简单介绍一下如何完成向联系人中添加信息等基本操作。

(1)启动模拟器,进入应用程序界面,如图11.1所示。

374-1 图11.1 Android应用程序界面

(2)单击“联系人”图标,打开联系人程序界面,如图11.2所示。由于并未在模拟器中添加联系人,因此显示“没有联系人”,此时提供了3种选择方式。

374-2 图11.2 Android联系人程序界面

(3)在图11.2中,单击“创建新联系人”按钮,弹出如图11.3所示的提示信息。

374-3 图11.3 提示信息界面

(4)在图11.3中,单击“本地保存”按钮,即可添加联系人信息,如图11.4所示。单击左上角的“完成”按钮,完成联系人的添加。

374-4 图11.4 添加联系人

(5)请读者自行添加联系人信息,以便后面应用程序测试。

11.2.7 范例2:查询联系人ID和姓名

例11.1 在Eclipse中创建Android项目,名称为11.1,实现查询当前联系人应用中联系人的ID和姓名。(实例位置:光盘\TM\sl\11\11.1)

(1)修改res\layout\main.xml文件,设置背景图片和标签属性,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:background="@drawable/background"
  6. android:orientation="vertical" >
  7. <TextView
  8. android:id="@+id/result"
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. android:textColor="@android:color/black"
  12. android:textSize="25dp" />
  13. </LinearLayout>

(2)创建RetrieveDataActivity类,该类继承了Activity类。在onCreate()方法中获得布局文件中定义的标签,在自定义的getQueryData()方法中获得查询数据,代码如下:

  1. public class RetrieveDataActivity extends Activity {
  2. private String[] columns = { Contacts._ID, //希望获得ID值
  3. Contacts.DISPLAY_NAME, //希望获得姓名
  4. };
  5. @Override
  6. public void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. TextView tv = (TextView) findViewById(R.id.result); //获得布局文件中的标签
  10. tv.setText(getQueryData()); //为标签设置数据
  11. }
  12. private String getQueryData() {
  13. StringBuilder sb = new StringBuilder(); //用于保存字符串
  14. ContentResolver resolver = getContentResolver(); //获得ContentResolver对象
  15. Cursor cursor = resolver.query(Contacts.CONTENT_URI, columns, null, null, null); //查询记录
  16. int idIndex = cursor.getColumnIndex(columns[0]); //获得ID记录的索引值
  17. int displayNameIndex = cursor.getColumnIndex(columns[1]); //获得姓名记录的索引值
  18. for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {//迭代全部记录
  19. int id = cursor.getInt(idIndex);
  20. String displayName = cursor.getString(displayNameIndex);
  21. sb.append(id + ": " + displayName + "\n");
  22. }
  23. cursor.close(); /关闭Cursor
  24. return sb.toString(); //返回查询结果
  25. }
  26. }

(3)在AndroidManifest文件中增加读取联系人记录的权限,代码如下:

  1. <uses-permission android:name="android.permission.READ_CONTACTS"/>

运行本实例,其效果如图11.5所示。

376-1 图11.5 显示联系人ID和姓名

11.3 自定义Content Provider

tb教学录像:光盘\TM\lx\11\自定义Content Provider.exe

如果开发人员希望共享自己的数据,则有以下两个选择:

[√]创建自定义的Content Provider(一个ContentProvider类的子类)。

[√]如果有预定义的provider,管理相同的数据类型并且有写入权限,则可以向其中增加数据。

前面已经详细介绍了如何使用系统预定义的Content Provider,下面将介绍如何自定义Content Provider。

如果自定义Content Provider,开发人员需要完成以下操作:

[√]建立数据存储系统。大多数Content Provider使用Android文件存储方法或者SQLite数据库保存数据,但是开发人员可以使用任何方式存储。Android提供了SQLiteOpenHelper类帮助创建数据库,SQLiteDatabase类帮助管理数据库。

[√]继承ContentProvider类来提供数据访问方式。

[√]在应用程序的AndroidManifest文件中声明Content Provider。

下面主要介绍继承ContentProvider类和声明Content Provider的操作。

11.3.1 继承ContentProvider类

开发人员定义ContentProvider类的子类,以便使用ContentResolver和Cursor类来共享数据。原则上,这意味着需要实现ContentProvider类定义的6个抽象方法,其语法格式如下:

  1. public boolean onCreate()
  2. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
  3. public Uri insert(Uri uri, ContentValues values)
  4. public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
  5. public int delete(Uri uri, String selection, String[] selectionArgs)
  6. public String getType(Uri uri)

各个方法的说明如表11.2所示。

表11.2 ContentProvider中的抽象方法及说明

方 法 说 明
onCreate() 用于初始化provider
query() 返回数据给调用者
insert() 插入新数据到Content Provider
update() 更新Content Provider中已经存在的数据
delete() 从Content Provider中删除数据
getType() 返回Content Provider数据的MIME类型

query()方法必须返回Cursor对象,用于遍历查询结果。Cursor自身是一个接口,Android提供了该接口的一些实现类,例如,SQLiteCursor能遍历存储在SQLite数据库中的数据。通过调用SQLiteDatabase类的query()方法可以获得Cursor对象,它们都位于android.database包中,其继承关系如图11.6所示。

377-1 图11.6 Cursor接口继承关系

说明:圆角矩形表示接口,矩形表示类。

由于这些ContentProvider方法能被位于不同进程和线程的不同ContentResolver对象调用,它们必须以线程安全的方式实现。

此外,开发人员也可以调用ContentResolver.notifyChange()方法,以便在数据修改时通知监听器。

除了定义子类自身,还应采取一些措施以简化客户端工作并让类更加易用:

(1)定义public static final Uri CONTENT_URI变量(CONTENT_URI是变量名称)。该字符串表示自定义的Content Provider处理的完整content:URI。开发人员必须为该值定义唯一的字符串。最佳的解决方式是使用Content Provider的完整类名(小写)。例如,EmployeeProvider的URI可能按如下方式定义:

  1. public static final Uri CONTENT_URI = Uri.parse("content://com.mingrisoft.employeeprovider");

如果provider包含子表,也应该为各个子表定义URI。这些URI应该有相同的authority(因为它标识Content Provider),使用路径进行区分,例如:

  1. content://com.mingrisoft.employeeprovider/dba
  2. content://com.mingrisoft.employeeprovider/programmer
  3. content://com.mingrisoft.employeeprovider/ceo

(2)定义Content Provider将返回给客户端的列名。如果开发人员使用底层数据库,这些列名通常与SQL数据库列名相同。同样,定义public static String常量,客户端用它们来指定查询中的列和其他指令。确保包含名为“_ID”的整数列来作为记录的ID值。无论记录中其他字段是否唯一,如URL,开发人员都应该包含该字段。如果打算使用SQLite数据库,_ID字段类型如下:

  1. INTEGER PRIMARY KEY AUTOINCREMENT

(3)仔细注释每列的数据类型,客户端需要使用这些信息来读取数据。

(4)如果开发人员正在处理新数据类型,则必须定义新的MIME类型,以便在ContentProvider. getType()方法实现中返回。

(5)如果开发人员提供的byte数据太大而不能放到表格中,如bitmap文件,提供给客户端的字段应该包含content:URI字符串。

11.3.2 声明Content Provider

为了让Android系统知道开发人员编写的Content Provider,应该在应用程序的AndroidManifest.xml文件中定义<provider>元素。没有在配置文件中声明的自定义Content Provider,对于Android系统不可见。

name属性的值是ContentProvider类的子类的完整名称;authorities属性是provider定义的content:URI中authority部分;ContentProvider的子类是EmployeeProvider。<provider>元素应该如下:

  1. <provider android:name="com.mingrisoft.EmployeeProvider"
  2. android:authorities="com.mingrisoft.employeeprovider"
  3. . . . />
  4. </provider>

注意:authorities属性删除了content:URI中的路径部分。

其他<provider>属性能设置读写数据的权限、提供显示给用户的图标或文本、启用或禁用provider等。如果数据不需要在多个运行的Content Provider间同步,则设置multiprocess为true。这允许在各个客户端进程创建一个provider实例,从而避免执行IPC。

11.4 经典范例

11.4.1 查询联系人姓名和电话

例11.2 在Eclipse中创建Android项目,名称为11.2,实现查询当前联系人应用中联系人的姓名和电话。(实例位置:光盘\TM\sl\11\11.2)

(1)修改res\layout\main.xml文件,设置背景图片和标签属性,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:background="@drawable/background"
  6. android:orientation="vertical" >
  7. <TextView
  8. android:id="@+id/result"
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. android:textColor="@android:color/black"
  12. android:textSize="25dp" />
  13. </LinearLayout>

(2)创建RetrieveDataActivity类,该类继承了Activity类。在onCreate()方法中获得布局文件中定义的标签,在自定义的getQueryData()方法中获得查询数据,代码如下:

  1. public class RetrieveDataActivity extends Activity {
  2. private String[] columns = { Contacts._ID, //获得ID值
  3. Contacts.DISPLAY_NAME, //获得姓名
  4. Phone.NUMBER, //获得电话
  5. Phone.CONTACT_ID, };
  6. public void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. TextView tv = (TextView) findViewById(R.id.result); //获得布局文件中的标签
  10. tv.setText(getQueryData()); //为标签设置数据
  11. }
  12. private String getQueryData() {
  13. StringBuilder sb = new StringBuilder(); //用于保存字符串
  14. ContentResolver resolver = getContentResolver(); //获得ContentResolver对象
  15. Cursor cursor = resolver.query(Contacts.CONTENT_URI, null, null, null, null);//查询记录
  16. while (cursor.moveToNext()) {
  17. int idIndex = cursor.getColumnIndex(columns[0]); //获得ID值的索引
  18. int displayNameIndex = cursor.getColumnIndex(columns[1]); //获得姓名索引
  19. int id = cursor.getInt(idIndex); //获得id
  20. String displayName = cursor.getString(displayNameIndex); //获得名称
  21. Cursor phone = resolver.query(Phone.CONTENT_URI, null, columns[3] + "=" + id, null, null);
  22. while (phone.moveToNext()) {
  23. int phoneNumberIndex = phone.getColumnIndex(columns[2]); //获得电话索引
  24. String phoneNumber = phone.getString(phoneNumberIndex); //获得电话
  25. sb.append(displayName + ": " + phoneNumber + "\n"); //保存数据
  26. }
  27. }
  28. cursor.close();/ //关闭Cursor
  29. return sb.toString();
  30. }
  31. }

(3)在AndroidManifest文件中增加读取联系人记录的权限,代码如下:

  1. <uses-permission android:name="android.permission.READ_CONTACTS"/>

运行本实例,其效果如图11.7所示。

380-1 图11.7 显示联系人姓名和电话

11.4.2 自动补全联系人姓名

例11.3 在Eclipse中创建Android项目,名称为11.3,实现自动补全联系人姓名的功能。(实例位置:光盘\TM\sl\11\11.3)

(1)修改res\layout\main.xml文件,设置背景图片和标签属性,并增加一个自动补全标签,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:background="@drawable/background"
  6. android:orientation="vertical" >
  7. <TextView
  8. android:id="@+id/title"
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. android:layout_gravity="center"
  12. android:text="@string/title"
  13. android:textColor="@android:color/black"
  14. android:textSize="30dp" />
  15. <LinearLayout
  16. android:layout_width="match_parent"
  17. android:layout_height="wrap_content"
  18. android:orientation="horizontal" >
  19. <TextView
  20. android:id="@+id/textView"
  21. android:layout_width="wrap_content"
  22. android:layout_height="wrap_content"
  23. android:layout_margin="5dp"
  24. android:text="@string/name"
  25. android:textColor="@android:color/black"
  26. android:textSize="25dp" />
  27. <AutoCompleteTextView
  28. android:id="@+id/edit"
  29. android:layout_width="match_parent"
  30. android:layout_height="wrap_content"
  31. android:completionThreshold="1"
  32. android:textColor="@android:color/black" >
  33. <requestFocus />
  34. </AutoCompleteTextView>
  35. </LinearLayout>
  36. </LinearLayout>

注意:android:completionThreshold属性用于设置输入几个字符时给出提示。

(2)创建ContactListAdapter类,它继承了CursorAdapter类并实现了Filterable接口,在重写方法时完成了获取联系人姓名的功能,代码如下:

  1. public class ContactListAdapter extends CursorAdapter implements Filterable {
  2. private ContentResolver resolver;
  3. private String[] columns = new String[] { Contacts._ID, Contacts.DISPLAY_NAME };
  4. public ContactListAdapter(Context context, Cursor c) {
  5. super(context, c); //调用父类构造方法
  6. resolver = context.getContentResolver(); //初始化ContentResolver
  7. }
  8. @Override
  9. public void bindView(View arg0, Context arg1, Cursor arg2) {
  10. ((TextView) arg0).setText(arg2.getString(1));
  11. }
  12. @Override
  13. public View newView(Context context, Cursor cursor, ViewGroup parent) {
  14. LayoutInflater inflater = LayoutInflater.from(context);
  15. TextView view = (TextView) inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
  16. view.setText(cursor.getString(1));
  17. return view;
  18. }
  19. @Override
  20. public CharSequence convertToString(Cursor cursor) {
  21. return cursor.getString(1);
  22. }
  23. @Override
  24. public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
  25. FilterQueryProvider filter = getFilterQueryProvider();
  26. if (filter != null) {
  27. return filter.runQuery(constraint);
  28. }
  29. Uri uri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(constraint.toString()));
  30. return resolver.query(uri, columns, null, null, null);
  31. }
  32. }

(3)创建AutoCompletionActivity类,它继承了Activity类,在重写onCreate()方法时,完成自动补全的设置,代码如下:

  1. public class AutoCompletionActivity extends Activity {
  2. private String[] columns = new String[] { Contacts._ID, Contacts.DISPLAY_NAME };
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.main);
  7. ContentResolver resolver = getContentResolver();
  8. Cursor cursor = resolver.query(Contacts.CONTENT_URI, columns, null, null, null);
  9. ContactListAdapter adapter = new ContactListAdapter(this, cursor);
  10. AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.edit);
  11. textView.setAdapter(adapter);
  12. }
  13. }

(4)在AndroidManifest文件中增加读取联系人记录的权限,代码如下:

  1. <uses-permission android:name="android.permission.READ_CONTACTS"/>

运行本实例,其效果如图11.8所示。

383-1 图11.8 自动补全联系人姓名

11.5 小 结

本章重点介绍了Android四大组件之一的Content Provider。Content Provider是所有应用程序之间数据存储和检索的一个桥梁。在Android中,Content Provider是一种特殊的数据存储类型,它提供了一套标准的方法来提供数据的增、删、改、查功能。本章详细介绍了实现各个功能需要使用的方法。此外,还介绍了如何自定义Content Provider。

11.6 实践与练习

  1. 编写Android程序,使用列表显示联系人ID和姓名。(答案位置:光盘\TM\sl\11\11.4)

  2. 编写Android程序,查询联系人姓名和电话,并按ID值降序排列。(答案位置:光盘\TM\sl\ 11\11.5)