ContentProvider 使用示例(转载)

简介:

ContentProvider 使用示例(转载)


 

当数据需要在应用程序间共享时,我们就可以利用ContentProvider为数据定义一个URI。之后其他应用程序对数据进行查询或者修改时,只需要从当前上下文对象获得一个ContentResolver(内容解析器)传入相应的URI就可以了。本节中将以前面创建的code.db数据库为例,向读者介绍如何定义一个ContentProvider,以及如何在其他程序中使用ContentResolver访问URI所指定的数据。 

9.3.1 定义ContentProvider(1) 

要为当前应用程序的私有数据定义URI,就需要专门定义一个继承自ContentProvider的类,然后根据不同的操作调用的方法去实现这些方法的功能。下面我们用SQLite2这个例子,来为它的数据库code.db定义一个URI。 

首先,在SQLite2的包中创建一个新类ContryCode.java,来装入所有与数据库操作有关的静态字段,以便于打包成JAR文件供其他应用程序调用。

[java]  view plain copy print ?
  1. package com.studio.android.chp9.ex3; 
  2.  
  3. import com.sun.jndi.toolkit.url.Uri; 
  4.  
  5. publicclass CountryCode { 
  6.     publicstaticfinal String DB_NAME = "code.db"
  7.     publicstaticfinal String TB_NAME = "countrycode"
  8.     publicstaticfinalint VERSION = 1
  9.     publicstaticfinal String ID = "_id"
  10.     publicstaticfinal String COUNTRY = "country"
  11.     publicstaticfinal String CODE = "code"
  12.     publicstaticfinal String AUTHORITY = "com.studio.andriod.provider.countrycode"
  13.     publicstaticfinalint ITEM = 1
  14.     publicstaticfinalint ITEM_ID = 2
  15.     publicstaticfinal String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.studio.android.countrycode"
  16.     publicstaticfinal String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.studio.android.countrycode"
  17.     publicstaticfinal Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/item"); 
  18.      
package com.studio.android.chp9.ex3;

import com.sun.jndi.toolkit.url.Uri;

public class CountryCode {
	public static final String DB_NAME = "code.db";
	public static final String TB_NAME = "countrycode";
	public static final int VERSION = 1;
	public static final String ID = "_id";
	public static final String COUNTRY = "country";
	public static final String CODE = "code";
	public static final String AUTHORITY = "com.studio.andriod.provider.countrycode";
	public static final int ITEM = 1;
	public static final int ITEM_ID = 2;
	public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.studio.android.countrycode";
	public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.studio.android.countrycode";
	public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/item");
	
}

其中DB_NAME、TB_NAME和VERSION分别定义了数据库和表的名称以及数据库的版本号。

ID、COUNTRY和CODE分别定义的是表中的各个列的列名。

AUTHORITY定义了标识ContentProvider的字符串

ITEM和ITEM_ID分别用于UriMatcher(资源标识符匹配器)中对路径item和item/id的匹配号码。

CONTENT_TYPE和CONTENT_ITEM_TYPE定义了数据的MIME类型。

 

需要注意的是,单一数据的MIME类型字符串应该以vnd.android.cursor.item/开头,数据集的MIME类型字符串则应该以vnd.android.cursor.dir/开头。CONTENT_URI定义的是查询当前表数据的content://样式URI。


接下来,同样是在SQLite2的包中创建一个继承自ContentProvider的类MyProvider.java,来实现对数据操作的各个方法。这里将用到MyHelper来辅助获得SQLiteDatabase对象,虽然也可以直接使用Context.OpenOrCreate()方法获得,但不及使用数据库打开辅助类方便。

[java]  view plain copy print ?
  1. import android.content.ContentProvider; 
  2. import android.content.UriMatcher; 
  3.  
  4. publicclass MyProvider extends ContentProvider { 
  5.     MyHelper dbHelper; 
  6.     privatestaticfinal UriMatcher sMatcher; 
  7.     static { 
  8.         sMatcher = new UriMatcher(UriMatcher.NO_MATCH); 
  9.         sMatcher.addURI(CountryCode.AUTHORITY, "item", CountryCode.ITEM); 
  10.         sMatcher.addURI(CountryCode.AUTHORITY, "item/#", CountryCode.ITEM_ID); 
  11.     } 
import android.content.ContentProvider;
import android.content.UriMatcher;

public class MyProvider extends ContentProvider {
	MyHelper dbHelper;
	private static final UriMatcher sMatcher;
	static {
		sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		sMatcher.addURI(CountryCode.AUTHORITY, "item", CountryCode.ITEM);
		sMatcher.addURI(CountryCode.AUTHORITY, "item/#", CountryCode.ITEM_ID);
	}
}

这里UriMatcher类型的静态字段是用来匹配传入到ContentProvider中的Uri的类。其构造方法传入的匹配码是使用match()方法匹配根路径时返回的值,这个匹配码可以为一个大于零的数表示匹配根路径或传入-1,即常量UriMatcher.NO_MATCH表示不匹配根路径。addURI()方法是用来增加其他URI匹配路径的,第一个参数传入标识ContentProvider的AUTHORITY字符串。第二个参数传入需要匹配的路径,这里的#代表匹配任意数字,另外还可以用*来匹配任意文本。第三个参数必须传入一个大于零的匹配码,用于match()方法对相匹配的URI返回相对应的匹配码。 

ContentProvider里针对数据的各种操作定义了6个抽象方法,下面是对各个方法的实现和讲解。

[java]  view plain copy print ?
  1. @Override 
  2.     publicboolean onCreate() { 
  3.         dbHelper = new MyHelper(getContext(), CountryCode.DB_NAME, null
  4.                 CountryCode.VERSION); 
  5.         returntrue
  6.     } 
@Override
	public boolean onCreate() {
		dbHelper = new MyHelper(getContext(), CountryCode.DB_NAME, null,
				CountryCode.VERSION);
		return true;
	}

每当ContentProvider启动时都会回调onCreate()方法。此方法主要进行一些ContentProvider初始化的工作,返回true表示初始化成功,返回false则初始化失败。在这个ContentProvider中,主要是构造了需要操作数据库的辅助类对象。

[java]  view plain copy print ?
  1. @Override 
  2.     public String getType(Uri uri) { 
  3.         switch (sMatcher.match(uri)) { 
  4.         case CountryCode.ITEM: 
  5.             return CountryCode.CONTENT_TYPE; 
  6.         case CountryCode.ITEM_ID: 
  7.             return CountryCode.CONTENT_ITEM_TYPE; 
  8.         default
  9.             thrownew IllegalArgumentException("Unknown URI " + uri); 
  10.         } 
  11.     } 
@Override
	public String getType(Uri uri) {
		switch (sMatcher.match(uri)) {
		case CountryCode.ITEM:
			return CountryCode.CONTENT_TYPE;
		case CountryCode.ITEM_ID:
			return CountryCode.CONTENT_ITEM_TYPE;
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
	}

getTyper()是用来返回数据的MIME类型的方法。使用sMatcher对URI进行匹配,并返回相应的MIME类型字符串,若无法匹配传入的URI,抛出IllegalArgumentException异常。

[java]  view plain copy print ?
  1. @Override 
  2.     publicint delete(Uri uri, String where, String[] args) { 
  3.         SQLiteDatabase db = dbHelper.getWritableDatabase(); 
  4.         int count; 
  5.         switch (sMatcher.match(uri)) { 
  6.         case CountryCode.ITEM: 
  7.             count = db.delete(CountryCode.TB_NAME, where, args); 
  8.             break
  9.         case CountryCode.ITEM_ID: 
  10.             String id = uri.getPathSegments().get(1); 
  11.             count = db.delete(CountryCode.TB_NAME, 
  12.                     CountryCode.ID 
  13.                             + "=" 
  14.                             + id 
  15.                             + (!TextUtils.isEmpty(where) ? " AND (" + where 
  16.                                     + ')' : ""), args); 
  17.             break
  18.         default
  19.             thrownew IllegalArgumentException("Unknown URI " + uri); 
  20.         } 
  21.         getContext().getContentResolver().notifyChange(uri, null); 
  22.         return count; 
  23.     } 
@Override
	public int delete(Uri uri, String where, String[] args) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		int count;
		switch (sMatcher.match(uri)) {
		case CountryCode.ITEM:
			count = db.delete(CountryCode.TB_NAME, where, args);
			break;
		case CountryCode.ITEM_ID:
			String id = uri.getPathSegments().get(1);
			count = db.delete(CountryCode.TB_NAME,
					CountryCode.ID
							+ "="
							+ id
							+ (!TextUtils.isEmpty(where) ? " AND (" + where
									+ ')' : ""), args);
			break;
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}
[java]  view plain copy print ?
  1. @Override 
  2.     publicint update(Uri uri, ContentValues values, String where, String[] args) { 
  3.         SQLiteDatabase db = dbHelper.getWritableDatabase(); 
  4.         int count; 
  5.         switch (sMatcher.match(uri)) { 
  6.         case CountryCode.ITEM: 
  7.             count = db.update(CountryCode.TB_NAME, values, where, args); 
  8.             break
  9.         case CountryCode.ITEM_ID: 
  10.             String id = uri.getPathSegments().get(1); 
  11.             count = db.update(CountryCode.TB_NAME, values, 
  12.                     CountryCode.ID 
  13.                             + "=" 
  14.                             + id 
  15.                             + (!TextUtils.isEmpty(where) ? " AND (" + where 
  16.                                     + ')' : ""), args); 
  17.             break
  18.         default
  19.             thrownew IllegalArgumentException("Unknown URI " + uri); 
  20.         } 
  21.         getContext().getContentResolver().notifyChange(uri, null); 
  22.         return count; 
  23.     } 
@Override
	public int update(Uri uri, ContentValues values, String where, String[] args) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		int count;
		switch (sMatcher.match(uri)) {
		case CountryCode.ITEM:
			count = db.update(CountryCode.TB_NAME, values, where, args);
			break;
		case CountryCode.ITEM_ID:
			String id = uri.getPathSegments().get(1);
			count = db.update(CountryCode.TB_NAME, values,
					CountryCode.ID
							+ "="
							+ id
							+ (!TextUtils.isEmpty(where) ? " AND (" + where
									+ ')' : ""), args);
			break;
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

delete()和update()方法分别用于数据的删除和修改操作,返回的是所影响数据的数目。我们这里两种方法的实现比较相似。首先利用数据库辅助对象获取一个SQLiteDatabase对象。然后根据传入的Uri用sMatcher进行匹配,对单个数据或数据集进行删除或修改,以便于在调用SQLiteDatabase对象的删除或修改方法时where语句中使用不同的表达式。这里通过调用getContext()方法获得调用update()方法的Context对象,再利用这个Context对象来获取一个ContentResolver的对象。notifyChange()方法则用来通知注册在此URI上的观察者(observer)数据发生了改变。最后返回删除或修改数据的行数。

[java]  view plain copy print ?
  1. @Override 
  2.     public Uri insert(Uri uri, ContentValues initialValues) { 
  3.         SQLiteDatabase db = dbHelper.getWritableDatabase(); 
  4.         long rowId; 
  5.         if (sMatcher.match(uri) != CountryCode.ITEM) { 
  6.             thrownew IllegalArgumentException("Unknown URI " + uri); 
  7.         } 
  8.         rowId = db.insert(CountryCode.TB_NAME, CountryCode.ID, initialValues); 
  9.         if (rowId > 0) { 
  10.             Uri noteUri = ContentUris.withAppendedId(CountryCode.CONTENT_URI, 
  11.                     rowId); 
  12.             getContext().getContentResolver().notifyChange(noteUri, null); 
  13.             return noteUri; 
  14.         } 
  15.         thrownew SQLException("Failed to insert row into " + uri); 
  16.     } 
@Override
	public Uri insert(Uri uri, ContentValues initialValues) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		long rowId;
		if (sMatcher.match(uri) != CountryCode.ITEM) {
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
		rowId = db.insert(CountryCode.TB_NAME, CountryCode.ID, initialValues);
		if (rowId > 0) {
			Uri noteUri = ContentUris.withAppendedId(CountryCode.CONTENT_URI,
					rowId);
			getContext().getContentResolver().notifyChange(noteUri, null);
			return noteUri;
		}
		throw new SQLException("Failed to insert row into " + uri);
	}

insert()方法用来插入数据,最后返回新插入数据的URI。在此方法的实现中,只接受数据集的URI,即指向表的URI。然后利用数据库辅助对象获得的SQLiteDatabase对象,调用insert()方法向指定表中插入数据。最后通知观察者数据发生变化,返回插入数据的URI。

[java]  view plain copy print ?
  1. @Override 
  2.     public Cursor query(Uri uri, String[] projection, String selection, 
  3.             String[] args, String order) { 
  4.         SQLiteDatabase db = dbHelper.getReadableDatabase(); 
  5.         Cursor c; 
  6.         switch (sMatcher.match(uri)) { 
  7.         case CountryCode.ITEM: 
  8.             c = db.query(CountryCode.TB_NAME, projection, selection, args, 
  9.                     nullnull, order); 
  10.             break
  11.         case CountryCode.ITEM_ID: 
  12.             String id = uri.getPathSegments().get(1); 
  13.             c = db.query(CountryCode.TB_NAME, projection, CountryCode.ID 
  14.                     + "=" 
  15.                     + id 
  16.                     + (!TextUtils.isEmpty(selection) ? " AND (" + selection 
  17.                             + ')' : ""), args, nullnull, order); 
  18.             break
  19.         default
  20.             thrownew IllegalArgumentException("Unknown URI " + uri); 
  21.         } 
  22.         c.setNotificationUri(getContext().getContentResolver(), uri); 
  23.         return c; 
  24.     } 
@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] args, String order) {
		SQLiteDatabase db = dbHelper.getReadableDatabase();
		Cursor c;
		switch (sMatcher.match(uri)) {
		case CountryCode.ITEM:
			c = db.query(CountryCode.TB_NAME, projection, selection, args,
					null, null, order);
			break;
		case CountryCode.ITEM_ID:
			String id = uri.getPathSegments().get(1);
			c = db.query(CountryCode.TB_NAME, projection, CountryCode.ID
					+ "="
					+ id
					+ (!TextUtils.isEmpty(selection) ? " AND (" + selection
							+ ')' : ""), args, null, null, order);
			break;
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}

 

query()是对数据进行查询的方法,最终将查询的结果包装入一个Cursor对象并返回。其实现首先还是通过数据库辅助对象获取一个SQLiteDatabase对象,然后使用sMatcher对传入URI进行匹配,并分别对单数据和多数据的URI构造不同的where语句表达式并查询。setNotificationUri()方法是用来为Cursor对象注册一个观察数据变化的URI。


实现完这几个抽象方法后,一个完整的ContentProvider就被定义好了,剩下的就是在Android-Manifest.xml中对这个ContentProvider的声明了。在AndroidManifest.xml中添加如下声明代码。 

<provider android:name="MyProvider" 
android:authorities="com.studio.andriod.provider.countrycode"/> 
其中android:name需要设置成刚才定义ContentProvider类的类名,android:authorities则指定了在content://样式的URI中标识这个ContentProvider的字符串。另外,可以设置android:readPermission和android:writePermission这两个属性, 来分别指定对这个ContentProvider中数据读和写操作的权限。也可以在onCreate()方法的实现中调用setRead-Permission()和setWritePermission()方法来动态指定权限。 

为了让其他程序更加方便地使用我们自己定义的ContentProvider,一般会将要用到的静态数据导出成JAR归档文件。如本程序中将所有要用到的静态字段都放在了类CountryCode中。下面是将其打包成JAR文件的步骤。 

(1) 在项目文件夹上或CountryCode.java文件上右键,选择Export..
(2) 在弹出的选择导出类型的对话框中,点击选择JAR file,然后点Next
(3) 在选择要导出的文件框中,只选择CountryCode.java。找到Browse...按钮指定导出路径(默认为workspace),点选Finish       完成导出CountryCode.jar


导出了JAR文件后,若需要在其他应用程序中使用,只需要为该项目添加外部的归档文件即可。在该项目文件夹上右键找到Build path→Add External Archive,然后选择要添加的JAR文件就可以在程序中使用import语句导入归档文件中的内容了。


ContentProvider是什么时候创建的,是谁创建的

访问某个应用程序共享的数据,是否需要启动这个应用程序?这个问题在 Android SDK中没有明确说明,但是从数据共享的角度出发,ContentProvider应该是Android在系统启动时就创建了,否则就谈不上数据共享了。 这就要求在AndroidManifest.XML中使用元素明确定义。

可能会有多个程序同时通过ContentResolver访问一个ContentProvider,会不会导致像数据库那样的“脏数据”?这个问题一方面需要数据库访问的同步,尤其是数据写入的同步,在AndroidManifest.XML中定义ContentProvider的时 候,需要考虑是元素multiprocess属性的值;另外一方面Android在ContentResolver中提供了notifyChange() 接口,在数据改变时会通知其他ContentObserver,这个地方应该使用了观察者模式,在ContentResolver中应该有一些类似 register,unregister的接口。 

至此,已经对ContentProvider提供了比较全面的分析,至于如何创建ContentProvider,可创建一个属于你自己的 ContentProvider或者将你的数据添加到一个已经存在的ContentProvider中,当然前提是有相同数据类型并且有写入 Content provider的权限


本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/p/3291018.html,如需转载请自行联系原作者


目录
相关文章
|
7月前
|
存储 缓存 Java
ContentProvider的启动
ContentProvider的启动
84 1
|
7月前
|
存储 数据库
ContentProvider的简单使用
ContentProvider的简单使用
45 1
|
7月前
|
存储 安全 Android开发
Android数据存储:请解释ContentProvider是什么,它的主要作用是什么?
ContentProvider是Android的四大组件之一,主要负责结构化数据的管理与共享。它封装数据并提供安全的访问接口,通过URI实现应用间数据的标准化共享。ContentResolver与ContentProvider协作,处理数据的CRUD操作,使得其他应用能方便地调用和操作数据。
58 0
|
Android开发 数据格式 XML
Android Activity之间的传值示例
版权声明:本文可能为博主原创文章,若标明出处可随便转载。 https://blog.
802 0
|
SQL 测试技术 数据库
09_Android中ContentProvider和Sqllite混合操作,一个项目调用另外一个项目的ContentProvider
1、  编写ContentPrivider提供者的Android应用 清单文件 &lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"     package="com.itheima28.sqlitede
1111 0