`
king_tt
  • 浏览: 2108920 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

【Android 开发教程】自定义ContentProvider

 
阅读更多

本章节翻译自《Beginning-Android-4-Application-Development》,如有翻译不当的地方,敬请指出。

原书购买地址http://www.amazon.com/Beginning-Android-4-Application-Development/dp/1118199545/


在Android平台上创建ContentProvider,相对而言是很容易的。你所需做的就是继承ContentProvider这个抽象类,然后重新它里面的各种方法。

下面将介绍如何创建一个ContentProvider,用来存储一些图书的信息。

1. 使用Eclipse创建一个工程,ContentProviders。

2. 新建一个名为BooksProvider类。

3. BooksProvider.java中的代码。

public class BooksProvider extends ContentProvider
{
	static final String PROVIDER_NAME =
		"net.manoel.provider.Books";

	static final Uri CONTENT_URI =
		Uri.parse("content://"+ PROVIDER_NAME + "/books");

	static final String _ID = "_id";
	static final String TITLE = "title";
	static final String ISBN = "isbn";

	static final int BOOKS = 1;
	static final int BOOK_ID = 2;

	private static final UriMatcher uriMatcher;
	static{
		uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		uriMatcher.addURI(PROVIDER_NAME, "books", BOOKS);
		uriMatcher.addURI(PROVIDER_NAME, "books/#", BOOK_ID);
	}

	//---for database use---
	SQLiteDatabase booksDB;
	static final String DATABASE_NAME = "Books";
	static final String DATABASE_TABLE = "titles";
	static final int DATABASE_VERSION = 1;
	static final String DATABASE_CREATE =
		"create table " + DATABASE_TABLE +
		" (_id integer primary key autoincrement, "
		+ "title text not null, isbn text not null);";

	private static class DatabaseHelper extends SQLiteOpenHelper
	{
		DatabaseHelper(Context context) {
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db)
		{
			db.execSQL(DATABASE_CREATE);
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion,
				int newVersion) {
			Log.w("Content provider database",
					"Upgrading database from version " +
							oldVersion + " to " + newVersion +
					", which will destroy all old data");
			db.execSQL("DROP TABLE IF EXISTS titles");
			onCreate(db);
		}
	}
	
	@Override
	public int delete(Uri arg0, String arg1, String[] arg2) {
		// arg0 = uri 
		// arg1 = selection
		// arg2 = selectionArgs
		int count=0;
		switch (uriMatcher.match(arg0)){
		case BOOKS:
			count = booksDB.delete(
					DATABASE_TABLE,
					arg1,
					arg2);
			break;
		case BOOK_ID:
			String id = arg0.getPathSegments().get(1);
			count = booksDB.delete(
					DATABASE_TABLE,
					_ID + " = " + id +
					(!TextUtils.isEmpty(arg1) ? " AND (" +
							arg1 + ')' : ""),
							arg2);
			break;
		default: throw new IllegalArgumentException("Unknown URI " + arg0);
		}
		getContext().getContentResolver().notifyChange(arg0, null);
		return count;
	}

	@Override
	public String getType(Uri uri) {
		switch (uriMatcher.match(uri)){
		//---get all books---
		case BOOKS:
			return "vnd.android.cursor.dir/vnd.manoel.books ";
			
		//---get a particular book---
		case BOOK_ID:
			return "vnd.android.cursor.item/vnd.manoel.books ";
			
		default:
			throw new IllegalArgumentException("Unsupported URI: " + uri);
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		//---add a new book---
		long rowID = booksDB.insert(
				DATABASE_TABLE,
				"",
				values);

		//---if added successfully---
		if (rowID>0)
		{
			Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
			getContext().getContentResolver().notifyChange(_uri, null);
			return _uri;
		}
		throw new SQLException("Failed to insert row into " + uri);
	}

	@Override
	public boolean onCreate() {
		Context context = getContext();
		DatabaseHelper dbHelper = new DatabaseHelper(context);
		booksDB = dbHelper.getWritableDatabase();
		return (booksDB == null)? false:true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder();
		sqlBuilder.setTables(DATABASE_TABLE);

		if (uriMatcher.match(uri) == BOOK_ID)
			//---if getting a particular book---
			sqlBuilder.appendWhere(
					_ID + " = " + uri.getPathSegments().get(1));

		if (sortOrder==null || sortOrder=="")
			sortOrder = TITLE;

		Cursor c = sqlBuilder.query(
			booksDB,
			projection,
			selection,
			selectionArgs,
			null,
			null,
			sortOrder);

		//---register to watch a content URI for changes---
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		int count = 0;
		switch (uriMatcher.match(uri)){
		case BOOKS:
			count = booksDB.update(
					DATABASE_TABLE,
					values,
					selection,
					selectionArgs);
			break;
		case BOOK_ID:
			count = booksDB.update(
					DATABASE_TABLE,
					values,
					_ID + " = " + uri.getPathSegments().get(1) +
					(!TextUtils.isEmpty(selection) ? " AND (" +
							selection + ')' : ""),
							selectionArgs);
			break;
		default: throw new IllegalArgumentException("Unknown URI " + uri);
		}
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}
}
4. 在AndroidManifest.xml中添加声明。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.manoel.ContentProviders"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="14" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name=".ContentProvidersActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <provider android:name="BooksProvider"
            android:authorities="net.manoel.provider.Books">            
        </provider>            
    </application>

</manifest>

在这个例子中,首先创建一个ContentProvider的子类,名字叫BooksProvider。需要重写一些方法:

  • getTpye() - 返回给定的URI数据的MIME类型
  • onCreate() - 当provider启动的时候被调用
  • query() - 接收客户的请求。返回一个Cursor对象。
  • insert() - 向provider中插入一条新记录
  • delete() - 在provider中删除一条已存在的记录
  • update() - 在provider中更新一条已存在的记录

在content provider中,可以任意选择要存储的数据 - 传统的文件,XML,数据库,甚至web service。


接下来,在BooksProvider.java类中定义一些常量:

	static final String PROVIDER_NAME =
		"net.manoel.provider.Books";

	static final Uri CONTENT_URI =
		Uri.parse("content://"+ PROVIDER_NAME + "/books");

	static final String _ID = "_id";
	static final String TITLE = "title";
	static final String ISBN = "isbn";

	static final int BOOKS = 1;
	static final int BOOK_ID = 2;

	private static final UriMatcher uriMatcher;
	static{
		uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		uriMatcher.addURI(PROVIDER_NAME, "books", BOOKS);
		uriMatcher.addURI(PROVIDER_NAME, "books/#", BOOK_ID);
	}

观察上面的代码,使用的一个UriMatcher对象去转换URI,这个URI将会作为一个参数传递给ContentResolver。举个例子,下面的URI代表了请求所有的图书:

content://net.manoel.provider.Books/books

下面的URI代表了请求指定_id 5为的图书:

content://net.manoel.provider.Books/books/5


content provider 使用一个SQlite数据库去存储图书。注意一点,这里使用SQLiteOpenHelper辅助类去操纵数据库:

private static class DatabaseHelper extends SQLiteOpenHelper
	{
		DatabaseHelper(Context context) {
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db)
		{
			db.execSQL(DATABASE_CREATE);
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion,
				int newVersion) {
			Log.w("Content provider database",
					"Upgrading database from version " +
							oldVersion + " to " + newVersion +
					", which will destroy all old data");
			db.execSQL("DROP TABLE IF EXISTS titles");
			onCreate(db);
		}
	}

接下来,重写getType()方法,唯一可以描述content provider的数据类型。使用UriMatcher对象,返回vnd.android.cursor.item/vnd.manoel.books代表单独的一本书,vnd.android.cursor.dir/vnd.android.cursor.item/vnd.manoel.bools 代表多本书:

	@Override
	public String getType(Uri uri) {
		switch (uriMatcher.match(uri)){
		//---get all books---
		case BOOKS:
			return "vnd.android.cursor.dir/vnd.manoel.books ";
			
		//---get a particular book---
		case BOOK_ID:
			return "vnd.android.cursor.item/vnd.manoel.books ";
			
		default:
			throw new IllegalArgumentException("Unsupported URI: " + uri);
		}
	}

然后,重写onCreate()方法,当provider被开启的时候,和数据库建立建立链接:

	@Override
	public boolean onCreate() {
		Context context = getContext();
		DatabaseHelper dbHelper = new DatabaseHelper(context);
		booksDB = dbHelper.getWritableDatabase();
		return (booksDB == null)? false:true;
	}

然后,重新query()方法:

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder();
		sqlBuilder.setTables(DATABASE_TABLE);

		if (uriMatcher.match(uri) == BOOK_ID)
			//---if getting a particular book---
			sqlBuilder.appendWhere(
					_ID + " = " + uri.getPathSegments().get(1));

		if (sortOrder==null || sortOrder=="")
			sortOrder = TITLE;

		Cursor c = sqlBuilder.query(
			booksDB,
			projection,
			selection,
			selectionArgs,
			null,
			null,
			sortOrder);

		//---register to watch a content URI for changes---
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}
查询的结果是一个Cursor对象。


然后,重写insert()方法:

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		//---add a new book---
		long rowID = booksDB.insert(
				DATABASE_TABLE,
				"",
				values);

		//---if added successfully---
		if (rowID>0)
		{
			Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
			getContext().getContentResolver().notifyChange(_uri, null);
			return _uri;
		}
		throw new SQLException("Failed to insert row into " + uri);
	}

一旦新的记录插入成功,调用ContentResolver的nofityChande()方法。这就通知观察者一个新的记录被更新了。


然后,重新delete()方法:

	@Override
	public int delete(Uri arg0, String arg1, String[] arg2) {
		// arg0 = uri 
		// arg1 = selection
		// arg2 = selectionArgs
		int count=0;
		switch (uriMatcher.match(arg0)){
		case BOOKS:
			count = booksDB.delete(
					DATABASE_TABLE,
					arg1,
					arg2);
			break;
		case BOOK_ID:
			String id = arg0.getPathSegments().get(1);
			count = booksDB.delete(
					DATABASE_TABLE,
					_ID + " = " + id +
					(!TextUtils.isEmpty(arg1) ? " AND (" +
							arg1 + ')' : ""),
							arg2);
			break;
		default: throw new IllegalArgumentException("Unknown URI " + arg0);
		}
		getContext().getContentResolver().notifyChange(arg0, null);
		return count;
	}

最后,重写update()方法:

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		int count = 0;
		switch (uriMatcher.match(uri)){
		case BOOKS:
			count = booksDB.update(
					DATABASE_TABLE,
					values,
					selection,
					selectionArgs);
			break;
		case BOOK_ID:
			count = booksDB.update(
					DATABASE_TABLE,
					values,
					_ID + " = " + uri.getPathSegments().get(1) +
					(!TextUtils.isEmpty(selection) ? " AND (" +
							selection + ')' : ""),
							selectionArgs);
			break;
		default: throw new IllegalArgumentException("Unknown URI " + uri);
		}
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

最后的最后,别忘了在AndroidManifest.xml中声明这个ContentProvider。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics