【Android(安卓)开发教程】自定义ContentProvider
16lz
2021-01-24
本章节翻译自《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);}@Overridepublic void onCreate(SQLiteDatabase db){db.execSQL(DATABASE_CREATE);}@Overridepublic 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);}}@Overridepublic int delete(Uri arg0, String arg1, String[] arg2) {// arg0 = uri // arg1 = selection// arg2 = selectionArgsint 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;}@Overridepublic 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);}}@Overridepublic 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);}@Overridepublic boolean onCreate() {Context context = getContext();DatabaseHelper dbHelper = new DatabaseHelper(context);booksDB = dbHelper.getWritableDatabase();return (booksDB == null)? false:true;}@Overridepublic 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;}@Overridepublic 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);}@Overridepublic void onCreate(SQLiteDatabase db){db.execSQL(DATABASE_CREATE);}@Overridepublic 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 代表多本书:
@Overridepublic 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被开启的时候,和数据库建立建立链接:
@Overridepublic boolean onCreate() {Context context = getContext();DatabaseHelper dbHelper = new DatabaseHelper(context);booksDB = dbHelper.getWritableDatabase();return (booksDB == null)? false:true;}
然后,重新query()方法:
@Overridepublic 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()方法:
@Overridepublic 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()方法:
@Overridepublic int delete(Uri arg0, String arg1, String[] arg2) {// arg0 = uri // arg1 = selection// arg2 = selectionArgsint 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()方法:
@Overridepublic 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。
更多相关文章
- Android(安卓)简单音乐播放器开发
- android应用:startActivityForResult使用实例
- android xml解析 XmlPullParser的使用
- Android(安卓)实现长按弹出PopupMenu 菜单栏
- Android(安卓)Handler机制6--消息的取出及分发
- 13-6-3 android 自定义tabhost在底部与框架函数的讲解2
- Android(安卓)native 内存泄露检测
- Android(安卓)-- Context
- Android遇到的问题解决方法