ContentProvider内部如何保存数据由其设计者决定。但是所有的ContentProvider都实现一组通用的方法,用来提供数据的增删改查功能。
客户端通常不会直接使用这些方法,大多数是通过ContentResolver对象实现对ContentProvider的操作,开发人员可以通过调用Activity或者其他应用程序组建的实现类中的getContentResolver()方法来获得ContentProvider对象。
使用ContentResolver提供的方法可以获得ContentProvider中任何感兴趣的数据。
当开始查询时,Android系统确认查询的目标ContentProvider并确保它正在运行。系统会初始化所有的ContentProvider类的对象,开发人员不必完成此类操作。实际上,开发人员根本不会直接使ContentProvider类的对象。通常,每个类型的ContentProvider仅有一个单独的实例。但是该实例能与位于不同应用程序和进程的多个ContentProvider类对象通信。不同进程之间的通信由ContentProvider类和ContentResolver类处理
ContentResolver使用基于数据库模型的简单表格来提供其中的 数据,这里每行代表一条记录,每列代表特定类型和含义的数据。例如,联系人的信息可能以表所示的方式提供。
——ID | NAME | NUMBER | |
001 | 王xx | 123****** | 123**@163.com |
002 | 张xx | 333****** | 333******@163.com |
查询返回一个Cursor对象,它能遍历各行各列来读取各个字段的值,对于各个类型的数据,它都提供了专用的方法。因此,为了读取字段的数据,开发人员必须知道当前字段包含的数据类型。
每个ContentProvider提供公共的URI(使用Uri类包装)来唯一标识其数据集。管理多个数据集(多个表格)的ContentProvider为每个数据集提供了单独的URI。所有为provider提供的URI都以“content://"作为前缀,”content://“模式表示数据由ContentProvider来管理。
如果自定义ContentProvider,则应该为其URI也定义一个常量,来简化客户端代码并让日后更新更加简洁。Android为当前平台提供的ContentProvider定义了CONTENT_URI常量。匹配电话号码到联系人表格的URI和匹配保存联系人照片表格的URI分别如下:
android.provider.Contacts.Phones.CONTENT_URI;
android.provider.Contacts.Photos.CONTETN_URI;
URI常量用于所有与ContentProvider的交互中。每个ContentProvider位方法使用URI作为其第一个参数,它标识ContentProvider应该使用哪个provider及其中的哪个表格。
下面是ContentUri重要部分的总结:
content://com.example.tofun/dba/001
1.红色(content://):标准的前缀,用于标识改数据由ContentProvider管理。它永远不用修改。
2.蓝色(com.example.tofun):URI的authority部分,它标识ContentProvider。对于第三方应用,该部分应该是完整的类名(使用小写形式)来保证唯一性。在
3.绿色(/dba):ContentProvider的路径部分,用于决定哪类数据被请求。如果ContentProvider仅提供一种数据类型,这部分可以省略。如果provider提供几种数据类型,包括子类型,这部分可以由几部分组成。
4.黄色(/001):被请求的特定记录的ID值。这是被请求记录的ID值。如果请求不仅限于单条记录,则该部分应该删除。
Android系统为常用数据类型提供了很多预定义的ContentProvider(声音,视频,图片,联系人等),它们大都位于android.provider包中。开发人员可以查询这些provider以获得其中包含的信息(尽管有些需要适当的权限来读取数据)。Android系统提供的常见ContentProvider说明如下:
1.Browser:读取或修改书签,浏览历史或网络搜索。
2.CallLog:查看或更新通话历史。
3.Contacts:获取,修改或保存联系人信息。
4.LiveFolders:由ContentProvider提供内容的特定文件夹。
5.MediaStore:访问声音,视频和图片。
6.Setting:查看和获取蓝牙设置,铃声和其他设备偏好。
7.SearchRecentSuggestions:能被配置以使用查找意见provider操作。
8.SyncStateContact:用于使用数据数组账号关联数据的ContentProvider约束。希望使用标准方式保存数据的provider可以使用它。
9.UserDictionary:在可预测文本输入谁,提供用户定义单词给输入法使用。应用程序和输入法能增加数据到该字典。单词能关联频率信息和本地化信息。
开发人员需要下面3条信息才能查询Contentprovider中的数据:
1.标识改ContentProvider的URI。
2.需要查询的数据字段名称。
3.字段中数据的类型。
如果查询特定的记录,则还需要提供该记录的ID值。
为了查询ContentProvider中do数据,开发人员需要使用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将是:
content://..../10
有些辅助方法,特别是ContentUris.withAppednedId()和Uri.withAppednedPath(),能轻松将ID增加到URI。这两个方法都是静态方法并返回一个增加了ID的URI对象。
query和managedQuery()方法的其他参数用来更加细致地限制查询结果,它们是:
1.应该返回的数据列名称。null值表示返回全部列:否则,仅返回列出的列。全部预定义ContentProvider都为其列定义了常量。例如,android.provider.Contacts.Phones类定义了_ID,NUMBER,NUMBER_KEY,NAME等常量
2.决定哪些行被返回的过滤器,格式类似SQL的WHERE语句(但是不包含WHERE自身)。null表示返回全部行(除非URI限制查询结果为单行记录)。
3.选择参数。
4.返回记录的排序器,格式类似SQL的ORDER BY语句(但是不包含ORDER BY自身)。null表示以默认顺序返回,可能是无序的。
查询返回一组零条或多条数据库记录。列明,默认顺序和数据类型对每个ContentProvider都是特别的。但是每个provider都有一个_ID列,它为每条记录保存唯一的数组ID。每个provider也能使用_COUNT报告返回结果中记录的行数,改制在各行都是相同的。
获得数据使用Cursor对象处理,它能向前或者向后遍历整个结果集。
为了向ContentProvider中增加新数据,首先需要在ContentValues对象中建立键值对映射,这里每个键匹配ContentProvider中的列名,每个值是该列中希望增加的值。然后调用ContentResolver.insert()方法并传递给它provider的URI参数和ContentValues的映射。该方法返回新纪录的完整URI,既增加了新纪录的ID的URI。
一旦记录存在,开发人员可以向其增加新信息或者修改已经存在的信息。增加记录到Contacts数据库的最佳方式是增加保存新数据的表名到代表记录的URI,然后使用组装好的URI来增加新数据。每个Contacts表格以CONTENT_DIRECTORY常量的方式提供名称作为该用途。
如果开发人员希望共享自己的数据,则有两个选择:
1.创建自定义的ContentProvider(一个ContentProvider的子类)
2.如果有预定义的ContentProvider,管理相同的数据类型并且有写入权限,则可以向其中增加数据。
如果自定义Contentprovider,则需要完成以下操作:
1.建立数据存储系统。大多数ContentProvider使用Android文件存储方法或者SQLite数据库保存数据,但是开发人员可以使用任何方式存储。Android提供了SQLiteOpenHelper类帮助创建数据库,SQLiteDatabase类管理数据库。
2.继承ContentProvider类来提供数据访问方式。
3.在应用程序清单文件中声明ContentProvider
下面介绍后两个任务。
定义ContentProvider类的子类,以便使用ContentResolver和Cursor类带来的便捷来共享数据。原则上,这意味着需要实现ContentProvider类定义的6个方法。
1.public boolean onCreate():用于初始化provider
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):返回ContentProvider数据的MIME类型。
在AndroidManifest.xml文件中定义
name属性的值是ContentProvider类的子类的完整名称;authorities属性是provider定义的content:URI中authority部分,例如
其他下面一个读取联系人ID和姓名的例子
public class MainActivity extends Activity {
private TextView tv;
String[] columns = {Contacts._ID,Contacts.DISPLAY_NAME};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView)findViewById(R.id.tv);
tv.setText(getQueryData());
}
private String getQueryData() {
StringBuffer sb = new StringBuffer();
ContentResolver resolver = this.getContentResolver();
Cursor cursor = resolver.query(Contacts.CONTENT_URI, columns, null, null, null);
int idIndex = cursor.getColumnIndex(columns[0]);
int nameIndex = cursor.getColumnIndex(columns[1]);
for(cursor.moveToFirst();!cursor.isAfterLast();cursor.moveToNext()){
int id = cursor.getInt(idIndex);
String name = cursor.getString(nameIndex);
sb.append(id+":"+name+"\n");
}
return sb.toString();
}
}
需要声明读取联系人的权限