做为Android四大组件之一的ContentProvider,主要用于应用程序间数据共享。日常的开发中更多的是使用getContentResolver操做系统的多媒体数据库(MediaProvider)。本文主要讲述如何自定义ContentProvider及注意事项。java
public class SimpleContentProvider extends ContentProvider { private static final String TAG = "SimpleContentProvider"; // 若不匹配采用UriMatcher.NO_MATCH(-1)返回 private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH); private static final int CODE_NOPARAM = 1; //无参 // 匹配码 private static final int CODE_PARAM = 2; //带参数 static { // 对等待匹配的URI进行匹配操做,必须符合com.test.providers.SimpleContentProvider/artical格式 // 匹配返回CODE_NOPARAM,不匹配返回-1 MATCHER.addURI("com.test.providers.SimpleContentProvider", "artical", CODE_NOPARAM); // #表示数字 com.test.providers.SimpleContentProvider/artical/10 // 匹配返回CODE_PARAM,不匹配返回-1 MATCHER.addURI("com.test.providers.SimpleContentProvider", "artical/#", CODE_PARAM); } private DBOpenHelper dbOpenHelper; //操做数据库 private final String ARTICAL_TABLE = "artical"; @Override public boolean onCreate() { // TODO Auto-generated method stub dbOpenHelper = new DBOpenHelper(getContext()); return true; } @Override public Uri insert(Uri uri, ContentValues cv) { // TODO Auto-generated method stub Log.i(TAG, "insert()"); SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); switch (MATCHER.match(uri)) { case CODE_NOPARAM: long id = db.insert(ARTICAL_TABLE, null, cv); //若主键是自增的,则返回主键值;不然为行号 Uri insertUri = ContentUris.withAppendedId(uri, id); return insertUri; default: throw new IllegalArgumentException("This is unknow uri: " + uri); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // TODO Auto-generated method stub Log.i(TAG, "delete()"); SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); switch (MATCHER.match(uri)) { case CODE_NOPARAM: return db.delete(ARTICAL_TABLE, selection, selectionArgs); //删除全部记录 case CODE_PARAM: long id = ContentUris.parseId(uri); //取得Uri后面的数字 String where = "_id = " + id; if(null != selection && !(selection.trim()).equals("")){ where += "and " + selection; } return db.delete(ARTICAL_TABLE, where, selectionArgs); default: throw new IllegalArgumentException("This is unknow uri: " + uri); } } @Override public int update(Uri uri, ContentValues cv, String selection, String[] selectionArgs) { // TODO Auto-generated method stub Log.i(TAG, "update()"); SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); switch (MATCHER.match(uri)) { case CODE_NOPARAM: return db.update(ARTICAL_TABLE, cv, selection, selectionArgs); //更新全部记录 case CODE_PARAM: long id = ContentUris.parseId(uri); //取得Uri后面的数字 String where = "_id = " + id; if(null != selection && !(selection.trim()).equals("")){ where += " and " + selection; } return db.update(ARTICAL_TABLE, cv, where, selectionArgs); default: throw new IllegalArgumentException("This is unknow uri: " + uri); } } /** * 返回对应的内容类型 * 若是返回集合的内容类型,必须以com.test.android.cursor.dir开头 * 若是是单个元素,必须以com.test.android.cursor.item开头 */ @Override public String getType(Uri uri) { // TODO Auto-generated method stub Log.i(TAG, "getType()"); switch (MATCHER.match(uri)) { case CODE_NOPARAM: return "com.test.android.cursor.dir/artical"; case CODE_PARAM: return "com.test.android.cursor.item/artical"; default: throw new IllegalArgumentException("This is unknow uri: " + uri); } } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) { // TODO Auto-generated method stub Log.i(TAG, "query()"); SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); switch (MATCHER.match(uri)) { case CODE_NOPARAM: return db.query(ARTICAL_TABLE, projection, selection, selectionArgs, null, null, orderBy); case CODE_PARAM: long id = ContentUris.parseId(uri); //取得Uri后面的数字 String where = "_id = " + id; if(null != selection && !(selection.trim()).equals("")){ where += "and " + selection; } return db.query(ARTICAL_TABLE, projection, where, selectionArgs, null, null, orderBy); default: throw new IllegalArgumentException("This is unknow uri: " + uri); } } }
该类提供了基本的增删改查方法,能够看出,实际操做的是数据库。其中DBOpenHelper类是一个继承自SQLiteOpenHelper的自定义类,里面包含基本的数据库建立、表的建立等操做,比较简单,此处就不贴代码了。android
一、已经建立了自定义类,接下来须要将其注册到AndroidManifest.xml中:数据库
<!-- 自定义的一些权限,须要发布才能被其余应用使用 -->
<permission android:name="android.permission.WRITE_SCP" />
<permission android:name="android.permission.READ_SCP"/>
<provider android:name="com.test.contentprovider.SimpleContentProvider" android:authorities="com.test.providers.SimpleContentProvider" android:exported="true" android:readPermission="android.permission.READ_SCP" android:writePermission="android.permission.WRITE_SCP" > <path-permission android:pathPrefix="/search_query" android:readPermission="android.permission.GLOBAL_SEARCH" /> </provider>
说明:ide
android:exported="true",容许在其余应用中访问该Provider;若设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。
android:authorities 受权信息,用于区分不一样的ContentProvider
android:permission 声明访问provider的权限测试
将权限细化,可分为下述两个:
android:readPermission 表示读权限,
android:writePermission 写权限spa
【疑问】细化后的权限比permission优先级高。
可是,测试结果显示:仅添加readPermission或writePermission权限,在另外一应用中读写provider均可以,那细化的做用在哪里?在网上也没有找到相关的介绍,谁若知道,可留言告知,谢谢!操作系统
二、既然声明了权限,那么其余应用访问该provider时,也须要AndroidManifest.xml申请相应权限,不然没法访问。code
<uses-permission android:name="android.permission.READ_SCP"/> <uses-permission android:name="android.permission.WRITE_SCP"/>
三、程序中访问:xml
Uri uri = Uri.parse("content://com.test.providers.SimpleContentProvider/artical"); ContentResolver resolver = getContentResolver(); ContentValues values = new ContentValues(); values.put("title", "testcase1 "); values.put("content", "ContentProvider-put"); resolver.insert(uri, values);
以上便是一个简单的访问自定义ContentProvider的例子。blog
一般状况下,ContentProvider存储数据操做的仍是数据库,只是进行了封装。不管是自定义类仍是系统原生的类,都是采用ContentProvider + SQLiteOpenHelper实现的。(自定义类继承ContentProvider,自定义类继承SQLiteOpenHelper实现对应数据库操做。)例:系统的MediaProvider、SettingsProvider或者上述自定义SimpleContentProvider均是如此。
可是,这并不表示ContentProvider只能操做数据库,也能够是文件等,不过,我没测试过,感兴趣的可自行验证。
总结Cursor遍历ContentProvider时,经常使用的几种方式:其实就是for()与while的使用。
1 ContentResolver resolver = getContentResolver(); 2 Cursor cursor = resolver.query(uri, null, null, null, null); 3 if(cursor != null && cursor.getCount() > 0){ 4 .....//遍历代码位置 5 } 6 7 方法一: 8 for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()){ 9 ...... 10 } 11 12 方法二: 13 while(cursor.moveToNext()){ 14 ....//具体操做 15 } 16 17 方法三: 18 19 cursor.moveToFirst(); //移动到第一行;默认cursor下标在-1 20 while(!cursor.isAfterLast()){ 21 ....... 22 .......//具体操做 23 cursor.moveToNext(); 24 } 25 26 方法四: 27 cursor.moveToFirst(); 28 do{ 29 ...... 30 }while(cursor.moveToNext());
上述代码看似很简单,可是在自测的过程却出现了奇怪的问题:
使用while偶现几回丢失数据的状况(即当前有三条数据,只读取了两条;可是此时(使用while)若将记录的id进行读取则能成功读取三条数据),具体缘由未找到,以后又屡次测试则没法复现了,所以在此处先记下该种状况,有知道的能够留言告知,谢谢。