内容提供器
在Android安全模型中,一个应用程序编写的文件无法被其他任何应用程序所读写。每个应用程序都有自己的Linux用户ID和数据目录,以及其受保护的内存空间。Android程序可以通过下面两种方式进行彼此间的通信。
第一种是IPC(Inter-Process Communication,进程间通信):一个进程使用AIDL(Android Interface Definition Language,接口定义语言)和IBinder接口声明一个任意的API。调用该API时,将在进程间安全且有效地队参数进行编组。这项先进技术用于对后台Service线程进行远程过程调用。
第二种就是ContentProvider:进程在系统中将他们本身注册为某些数据类型的提供者。请求该信息时,Android就会通过一个固定的API调用这些进程,以它们认为合适的方式查询或者修改内容。
ContentProvider管理的任何信息部分都通过一个URI来寻址,这个URI类似于以下形式:content://authority/path/id
其中content://是标准要求的前缀。authority是提供者的名称,建议使用完全限定包名称,避免出现名称冲突。Path是提供者内部的一个虚拟目录,用于标识被请求的数据类型。Id是被请求的特定记录的主键,要请求获得具有特定类型的所有记录,可以省略此参数以及后面的斜杠。
Android已经内置提供了几个提供者,包括:
content://browser;
content://contacts;
content://media;
content://settings。
下面我们就来演示如何使用内容提供器。
将HelloSQLite程序改为使用内容提供器的。对于HelloSQLite的提供者,下面都是有效的URI:
content://com.blueeagle.HelloSQLite/3 ——表示取id为3的数据
content://com.blueeagle.HelloSQLite/ ——表示取所有数据
首先需要向Contants.java添加两个常量:
public static final String AUTHORITY = "com.blueeagle.HelloSQLite";
public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);
接下来可以对HelloSQLite类做一些修改:
代码如下:
- package com.blueeagle;
-
-
- import android.app.ListActivity;
-
- import android.content.ContentValues;
-
- import android.database.Cursor;
-
- import android.os.Bundle;
-
- import android.widget.SimpleCursorAdapter;
-
- import static com.blueeagle.Constants.TIME;
-
- import static com.blueeagle.Constants.TITLE;
-
- import static com.blueeagle.Constants.CONTENT_URI;
-
- import static android.provider.BaseColumns._ID;
-
-
-
- public class HelloSQLite extends ListActivity {
-
- /** Called when the activity is first created. */
-
- //private MySQLite mySqlite;
-
- private static String[] FROM = {_ID,TIME,TITLE};
-
- private static String ORDER_BY = TIME+" DESC";
-
- //@Override
-
- public void onCreate(Bundle savedInstanceState) {
-
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.main);
-
-
-
- addItem("Hello,Android!");
-
- Cursor cursor = getItems();
-
- showItems(cursor);
-
- }
-
- private static int[] TO = {R.id.rowid,R.id.time,R.id.title};
-
-
-
- //显示查询结果
-
- private void showItems(Cursor cursor) {
-
- // TODO Auto-generated method stub
-
- SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,R.layout.item,cursor,FROM,TO);
-
- setListAdapter(adapter);
-
- }
-
- //使用ContentProvider时,geiItems方法也化简了
-
- private Cursor getItems() {
-
- return managedQuery(CONTENT_URI,FROM,null,null,ORDER_BY);
-
- }
-
- //此处使用Activity.managedQuery()方法,将内容URI,感兴趣的列表和应该使用的排序顺序传递给该方法。
-
- private void addItem(String string) {
-
- ContentValues values = new ContentValues();
-
- values.put(TIME, System.currentTimeMillis());
-
- values.put(TITLE, string);
-
- getContentResolver().insert(CONTENT_URI, values);
-
- }
-
- //没有了对getWriteableDatabase()的调用,对insertOrThrow的调用替换成了getContentResolver().insert(CONTENT_URI, values)
-
- //我们使用了一个URI而不是用数据库句柄。
-
- }
下面就是实现ContentProvider
ContentProvider是一个类似于Activity的高级对象,需要向系统进行声明。因此,实现ContentProvider的第一步是将其添加到AndroidManifest.xml文件中的<activity>标签之前。
其中android:name是类名,android:authorities是在内容URI中使用的字符串。接下来创建我们自己的ContentProvider类,为继承类。ContentProvider创建后就会被调用:public boolean onCreate()ContentProvider类中有6个继承而来的方法,需要实现:
具体来说需要实现ContentProvider 类中的6个抽象方法。
Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):将查询的数据以Cursor 对象的形式返回。
Uri insert(Uri uri, ContentValues values):向 Content Provider中插入新数据记录,ContentValues 为数据记录的列名和列值映射。
int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):更新Content Provider中已存在的数据记录。
int delete(Uri uri, String selection, String[] selectionArgs):从Content Provider中删除数据记录。
String getType(Uri uri):返回Content Provider中的数据( MIME )类型。
boolean onCreate():当 Content Provider 启动时被调用。
定义一个 URI 类型的静态常量,命名为CONTENT_URI。 必须为该常量对象定义一个唯一的URI字符串,一般的做法是将 ContentProvider子类的全称类名作为URI字符串。
定义每个字段的列名,如果采用的数据库存储系统为SQLite 数据库,数据表列名可以采用数据库中表的列名。不管数据表中有没有其他的唯一标识一个记录的字段,都应该定义一个"_id"字段 来唯一标识一个记录。模式使用 "INTEGER PRIMARY KEY AUTOINCREMENT" 自动更新 一般将这些列名字符串定义为静态常量, 如"_id"字段名定义为一个名为"_ID" 值为 "_id" 的静态字符串对象。
创建好的一个Content Provider必须在AndroidManifest.xml中声明。
<provider android:name=".ItemsProvider"
android:authorities="com.blueeagle" />
其中name属性为ContentProvider 子类的全称类名,authorities 属性唯一标识了一个ContentProvider。还可以通过 setReadPermission() 和 setWritePermission() 来设置其操作权限。当然也可以再上面的 xml中加入 android:readPermission 或者 android: writePermission属性来控制其权限。
注意:因为ContentProvider可能被不同的进程和线程调用,所以这些方法必须是线程安全的。
然后需要使用UriMatcher,用于匹配Uri。
用法如下:
首先把需要匹配Uri路径全部给注册上:
对于Uri:
什么是URI?将其分为A,B,C,D 4个部分:
A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://"
B:URI的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的 类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称 ;"content://hx.android.text.myprovider"
C:路径,不知道是不是路径,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就ok了;"content://hx.android.text.myprovider/tablename"
D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; "content://hx.android.text.myprovider/tablename/#" #表示数据id
注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,例如匹配content://com.blueeagle路径,返回的匹配码为1。
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//添加需要匹配的uri,如果匹配就会返回匹配码
//如果match()方法匹配content://com.blueeagle路径,返回匹配码为1
sUriMatcher.addURI(“content://com.blueeagle”, “HelloSQLite”, 1);
//如果match()方法匹配content://com.blueeagle/ ***路径,返回匹配码为2
//#号为通配符
sUriMatcher.addURI(“content://com.blueeagle”, “HelloSQLite/#”, 2);
switch(sUriMatcher.match(Uri.parse("content://com.blueeagle /***"))) {
case 1 break;
case 2 break;
default:
//不匹配 break;
}
自定义的contentprovider如下:
ItemsProvider.java
- package com.blueeagle;
-
- import android.content.ContentProvider;
-
- import android.content.ContentUris;
-
- import android.content.ContentValues;
-
- import android.content.UriMatcher;
-
- import android.database.Cursor;
-
- import android.database.sqlite.SQLiteDatabase;
-
- import android.net.Uri;
-
- import android.text.TextUtils;
-
- import static com.blueeagle.Constants.CONTENT_URI;
-
- import static com.blueeagle.Constants.TABLE_NAME;
-
- import static com.blueeagle.Constants.AUTHORITY;
-
- import static android.provider.BaseColumns._ID;
-
-
-
- public class ItemsProvider extends ContentProvider {
-
-
-
- private static final int ITEMS = 1;
-
- private static final int ITEMS_ID = 2;
-
- /** The MIME type of a directory of items */
-
- private static final String CONTENT_TYPE
-
- = "vnd.android.cursor.dir/vnd.com.blueeagle";
-
- /** The MIME type of a single item */
-
- private static final String CONTENT_ITEM_TYPE
-
- = "vnd.android.cursor.item/vnd.com.blueeagle";
-
-
-
- private MySQLite myDataBase ;
-
- private UriMatcher myUriMatcher;
-
-
-
- @Override
-
- public boolean onCreate() {
-
- myUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
- myUriMatcher.addURI(AUTHORITY, "HelloSQLite", ITEMS);
-
- myUriMatcher.addURI(AUTHORITY, "HelloSQLite/#", ITEMS_ID);
-
- myDataBase = new MySQLite(getContext());
-
- return true;
-
- }
-
- @Override
-
- public int delete(Uri uri, String selection,
-
- // TODO Auto-generated method stub
-
- String[] selectionArgs) {
-
- SQLiteDatabase db = myDataBase.getWritableDatabase();
-
- int count;
-
- switch (myUriMatcher.match(uri)) {
-
- case ITEMS:
-
- count = db.delete(TABLE_NAME, selection, selectionArgs);
-
- break;
-
- case ITEMS_ID:
-
- long id = Long.parseLong(uri.getPathSegments().get(1));
-
- count = db.delete(TABLE_NAME, appendRowId(selection, id),
-
- selectionArgs);
-
- break;
-
- default:
-
- throw new IllegalArgumentException("Unknown URI " + uri);
-
- }
-
- // Notify any watchers of the change
-
- getContext().getContentResolver().notifyChange(uri, null);
-
- return count;
-
- }
-
- @Override
-
- public String getType(Uri uri) {
-
- // TODO Auto-generated method stub
-
- switch (myUriMatcher.match(uri)) {
-
- case ITEMS:
-
- return CONTENT_TYPE;
-
- case ITEMS_ID:
-
- return CONTENT_ITEM_TYPE;
-
- default:
-
- throw new IllegalArgumentException("Unknown URI " + uri);
-
- }
-
- }
-
- @Override
-
- public Uri insert(Uri uri, ContentValues values) {
-
- // TODO Auto-generated method stub
-
- SQLiteDatabase db = myDataBase.getWritableDatabase();
-
- // Validate the requested uri
-
- if (myUriMatcher.match(uri) != ITEMS) {
-
- throw new IllegalArgumentException("Unknown URI " + uri);
-
- }
-
- // Insert into database
-
- long id = db.insertOrThrow(TABLE_NAME, null, values);
-
- // Notify any watchers of the change
-
- Uri newUri = ContentUris.withAppendedId(CONTENT_URI, id);
-
- getContext().getContentResolver().notifyChange(newUri, null);
-
- return newUri;
-
- }
-
- @Override
-
- public Cursor query(Uri uri, String[] projection,
-
- String selection, String[] selectionArgs, String orderBy) {
-
- // TODO Auto-generated method stub
-
- if (myUriMatcher.match(uri) == ITEMS_ID) {
-
- long id = Long.parseLong(uri.getPathSegments().get(1));
-
- selection = appendRowId(selection, id);
-
- }
-
- // Get the database and run the query
-
- SQLiteDatabase db = myDataBase.getReadableDatabase();
-
- Cursor cursor = db.query(TABLE_NAME, projection, selection,
-
- selectionArgs, null, null, orderBy);
-
- // Tell the cursor what uri to watch, so it knows when its
-
- // source data changes
-
- cursor.setNotificationUri(getContext().getContentResolver(),
-
- uri);
-
- return cursor;
-
- }
-
- private String appendRowId(String selection, long id) {
-
- // TODO Auto-generated method stub
-
- return _ID + "=" + id
-
- + (!TextUtils.isEmpty(selection)
-
- ? " AND (" + selection + ')'
-
- : "");
-
- }
-
- @Override
-
- public int update(Uri uri, ContentValues values, String selection,
-
- String[] selectionArgs) {
-
- // TODO Auto-generated method stub
-
- SQLiteDatabase db = myDataBase.getWritableDatabase();
-
- int count;
-
- switch (myUriMatcher.match(uri)) {
-
- case ITEMS:
-
- count = db.update(TABLE_NAME, values, selection,
-
- selectionArgs);
-
- break;
-
- case ITEMS_ID:
-
- long id = Long.parseLong(uri.getPathSegments().get(1));
-
- count = db.update(TABLE_NAME, values, appendRowId(
-
- selection, id), selectionArgs);
-
- break;
-
- default:
-
- throw new IllegalArgumentException("Unknown URI " + uri);
-
- }
-
- // Notify any watchers of the change
-
- getContext().getContentResolver().notifyChange(uri, null);
-
- return count;
-
- }
-
- }
总结一下,创建一个新的内容提供器。
1.通过扩展抽象类ContentProvider可以创建新的内容提供器。重写onCreate方法来打开或者初始化将要使用这个新的提供器来提供底层数据源。
2.还应该提供那些用来返回指向这个提供器的完整的URI的公共静态CONTENT_URI变量。提供器之间的内容URI应该是唯一的,所以最好的做法是使URI路径以包名为基础。
定义一个内容提供器URI一般的形式为:
content://com.pakagename/datapath
例如:content://com.blueeagle/items
或者:content://com.blueeagle./items/3
内容URI可以表示为这两种形式中的任意一种形式。前面的一种URI表示对那种类型中所有的值的请求,后面附加一个/3的表示对一条记录的请求(这里请求的是记录3)。这两种形式来访问提供器都是可行的。
完成这项工作最简单的方式是使用一个UriMatcher。当通过内容解析器来访问内容提供器的时候,可以配置UriMathcer来解析URI以确定它们的形式。就像前文说的那样
posted on 2013-02-20 10:41
游子 阅读(2970)
评论(0) 编辑 收藏 引用 所属分类:
软件