学习笔记——ContentProvider

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

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

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

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

1.1数据模型

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

——ID NAME NUMBER EMAIL
001 王xx 123****** 123**@163.com
002 张xx 333****** 333******@163.com
每条记录包含一个数值型的_ID字段,它用于表格中唯一标识改记录。ID能用于匹配相关表格中的记录,例如在一个表格中查询联系人电话,在另一表格中查询其照片。

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

1.2URI的用法

每个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。对于第三方应用,该部分应该是完整的类名(使用小写形式)来保证唯一性。在元素的authorities属性中声明authorty。

3.绿色(/dba):ContentProvider的路径部分,用于决定哪类数据被请求。如果ContentProvider仅提供一种数据类型,这部分可以省略。如果provider提供几种数据类型,包括子类型,这部分可以由几部分组成。

4.黄色(/001):被请求的特定记录的ID值。这是被请求记录的ID值。如果请求不仅限于单条记录,则该部分应该删除。

2.预定义ContentProvider

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:在可预测文本输入谁,提供用户定义单词给输入法使用。应用程序和输入法能增加数据到该字典。单词能关联频率信息和本地化信息。

2.1查询数据

开发人员需要下面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对象处理,它能向前或者向后遍历整个结果集。

2.2增加记录

为了向ContentProvider中增加新数据,首先需要在ContentValues对象中建立键值对映射,这里每个键匹配ContentProvider中的列名,每个值是该列中希望增加的值。然后调用ContentResolver.insert()方法并传递给它provider的URI参数和ContentValues的映射。该方法返回新纪录的完整URI,既增加了新纪录的ID的URI。

2.3增加新值

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

3.自定义ContentProvider

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

1.创建自定义的ContentProvider(一个ContentProvider的子类)

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

如果自定义Contentprovider,则需要完成以下操作:

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

2.继承ContentProvider类来提供数据访问方式。

3.在应用程序清单文件中声明ContentProvider

下面介绍后两个任务。

3.1继承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类型。

3.2声明ContentProvider

在AndroidManifest.xml文件中定义标签。没有在清单文件中声明,自定义的ContentProvider对于Android系统不可见。

name属性的值是ContentProvider类的子类的完整名称;authorities属性是provider定义的content:URI中authority部分,例如

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

下面一个读取联系人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();
	}
}
需要声明读取联系人的权限



你可能感兴趣的:(Android学习之旅)