十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
喜欢的小伙伴欢迎关注,我会定期分享Android知识点及解析,还会不断更新的BATJ面试专题,欢迎大家前来探讨交流,如有好的文章也欢迎投稿。
创新互联专注于企业营销型网站建设、网站重做改版、双河网站定制设计、自适应品牌网站建设、H5响应式网站、商城网站制作、集团公司官网建设、成都外贸网站建设、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为双河等各大城市提供网站开发制作服务。
ContentProvider,是自身APP开放给第三方APP的,用于访问自身数据库数据的接口。
第三方APP可以通过该接口,对指定的数据进行增删改查。
那么如何定义自身的ContentProvider
接口呢?
在回答问题之前,先来关注一下Uri。
原因在于,uri是ContentProvider解析外部请求(或者说是,第三方访问自身数据库)的关键参数。
Uri的字符串格式如下
content://package/table/id
例如
content://com.breakloop.sqlitehelperdemo/hero/1
从上方Uri示例中,可以获取到以下信息。
第三方APP想要
访问com.breakloop.sqlitehelperdemo
的数据库。至于哪个,由contentProvider
内部映射指定。
访问表hero。至于表名是不是真为hero,也由contentProvider
说了算。
访问id为1的数据。至于是不是id代表的具体含义,解释权也归contentProvider
。
那么,第三方APP将Uri传入后,ContentProvider
如何将其map为具体的数据库操作呢?
这便有了UriMatcher
工具类的引入。
该工具类,可以将Uri映射为int类型的行为代码。行为代码,可以看做是ContentProvider
自定义的枚举类型。而不同的行为代码,绑定不同的数据库操作。
我们先来看一下,Uri与行为代码的映射关系
public final static String AUTHORITY="com.breakloop.contentproviderdemo1"; public final static int BY_NAME=1; public final static int BY_AGE=2; public final static int BY_SEX=3; public final static int BY_NONE=0; public final static String PATH_BY_NAME=DBConst.TABLE_PERSON+"/ByName/*"; public final static String PATH_BY_AGE=DBConst.TABLE_PERSON+"/ByAge/#"; public final static String PATH_BY_SEX=DBConst.TABLE_PERSON+"/BySex/*"; }
static { matcher=new UriMatcher(UriMatcher.NO_MATCH); matcher.addURI(MatcherConst.AUTHORITY,MatcherConst.PATH_BY_NAME,MatcherConst.BY_NAME); matcher.addURI(MatcherConst.AUTHORITY,MatcherConst.PATH_BY_AGE,MatcherConst.BY_AGE); matcher.addURI(MatcherConst.AUTHORITY,MatcherConst.PATH_BY_SEX,MatcherConst.BY_SEX); matcher.addURI(MatcherConst.AUTHORITY,DBConst.TABLE_PERSON,MatcherConst.BY_NONE); }
在上面的示例中,UriMatch
绑定了四个Uri,并将各个Uri映射为四个行为代码。
其中,用到了转义符。#代表任意数字,*代表任意字母。
那么如何将行为代码映射为具体的数据库操作呢?,换句话说,在哪儿使用UriMatcher
呢?当然是ContentProvider
中!!!在ContentProvider
中的增删改查方法中,完成操作映射。
我们来看一下,ContentProvider
的创建。
先用Android Studio 创建一个ContentProvider
.
创建过程中,需要提供AUTHORITY,
ContentProvider
生成后,Android Studio将自动帮助ContentProvider
在Manifest中进行注册。
接下来,我们看看ContentProvider
的结构。
import android.content.ContentProvider;import android.content.ContentValues;import android.content.UriMatcher;import android.database.Cursor;import android.net.Uri;public class MyContentProvider extends ContentProvider { public MyContentProvider() { } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // Implement this to handle requests to delete one or more rows. throw new UnsupportedOperationException("Not yet implemented"); } @Override public String getType(Uri uri) { // TODO: Implement this to handle requests for the MIME type of the data // at the given URI. throw new UnsupportedOperationException("Not yet implemented"); } @Override public Uri insert(Uri uri, ContentValues values) { // TODO: Implement this to handle requests to insert a new row. throw new UnsupportedOperationException("Not yet implemented"); } @Override public boolean onCreate() { // TODO: Implement this to initialize your content provider on startup. return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // TODO: Implement this to handle query requests from clients. throw new UnsupportedOperationException("Not yet implemented"); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // TODO: Implement this to handle requests to update one or more rows. throw new UnsupportedOperationException("Not yet implemented"); } }
一共七个方法。包括一个构造方法,四个数据库方法(增删改查),一个初始化方法(onCreate),还有一个getType。
关于getType,我们之后解释。
关于构造方法,没什么可解释的。
关于初始化方法,当返回true时,表明初始化成功,否则,失败。由于我们要对数据库进行操作,因此,需要获取sqlite数据库对象。
public boolean onCreate() { helper=new MySQLiteHelper(getContext(),DBConst.DB_NAME,null,1); return true; }
关于数据库方法,我们看到传参中存在Uri,因此,这里需要用到UirMatcher
了。我们将刚才的UriMatcher
代码段,加入MyContentProvider
.这样,我们就可以在各个数据库方法中,解析Uri了。同时,由于sqlite
数据库对象的存在,进而可以对数据库进行相应操作。
我们先看一下最简单的插入操作。
public Uri insert(Uri uri, ContentValues values) { Uri returnUri=null; SQLiteDatabase db=helper.getWritableDatabase(); switch (matcher.match(uri)){ case MatcherConst.BY_NONE: long recordID=db.insert(DBConst.TABLE_PERSON,null,values); returnUri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/"+recordID); break; default: break; } return returnUri; }
再来看一下稍微复杂的查询。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor cursor=null; SQLiteDatabase db=helper.getReadableDatabase(); switch (matcher.match(uri)){ case MatcherConst.BY_NONE: cursor=db.query(DBConst.TABLE_PERSON,projection,selection,selectionArgs,null,null,sortOrder); break; case MatcherConst.BY_AGE: cursor=db.query(DBConst.TABLE_PERSON,projection,DBConst.COLUMN_AGE+"=?",new String[]{uri.getPathSegments().get(2)},null,null,sortOrder); break; case MatcherConst.BY_SEX: cursor=db.query(DBConst.TABLE_PERSON,projection,DBConst.COLUMN_SEX+"=?",new String[]{uri.getPathSegments().get(2)},null,null,sortOrder); break; case MatcherConst.BY_NAME: cursor=db.query(DBConst.TABLE_PERSON,projection,DBConst.COLUMN_NAME+"=?",new String[]{uri.getPathSegments().get(2)},null,null,sortOrder); break; default: break; } return cursor; }
这里需要注意的是,如何取Uri中的传入数据。使用的获取方法是uri.getPathSegments().get(index)
。该方法获取的是AUTHORITY后面的String部分。
然后,以”/”为分隔符,生成String[]。
接着是更新操作。
@Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int recordID=0; SQLiteDatabase db=helper.getWritableDatabase(); switch (matcher.match(uri)){ case MatcherConst.BY_NONE: recordID=db.update(DBConst.TABLE_PERSON,values,null,null); break; case MatcherConst.BY_AGE: recordID=db.update(DBConst.TABLE_PERSON,values,DBConst.COLUMN_AGE+"=?",new String[]{uri.getPathSegments().get(2)}); break; case MatcherConst.BY_SEX: recordID=db.update(DBConst.TABLE_PERSON,values,DBConst.COLUMN_SEX+"=?",new String[]{uri.getPathSegments().get(2)}); break; case MatcherConst.BY_NAME: recordID=db.update(DBConst.TABLE_PERSON,values,DBConst.COLUMN_NAME+"=?",new String[]{uri.getPathSegments().get(2)}); break; default: break; } return recordID; }
还有删除。
public int delete(Uri uri, String selection, String[] selectionArgs) { int recordID=0; SQLiteDatabase db=helper.getWritableDatabase(); switch (matcher.match(uri)){ case MatcherConst.BY_NONE: recordID=db.delete(DBConst.TABLE_PERSON,null,null); break; case MatcherConst.BY_AGE: recordID=db.delete(DBConst.TABLE_PERSON,DBConst.COLUMN_AGE+"=?",new String[]{uri.getPathSegments().get(2)}); break; case MatcherConst.BY_SEX: recordID=db.delete(DBConst.TABLE_PERSON,DBConst.COLUMN_SEX+"=?",new String[]{uri.getPathSegments().get(2)}); break; case MatcherConst.BY_NAME: recordID=db.delete(DBConst.TABLE_PERSON,DBConst.COLUMN_NAME+"=?",new String[]{uri.getPathSegments().get(2)}); break; default: break; } return recordID; }
那么,第三方如何调用ContentProvider呢?
这里,我们新建一个工程contentproviderdemo2,在必要的位置使用方法
public ContentResolver getContentResolver()
获取ContentProvider实例,之后便可传入Uri,调用数据库相关方法了。
例如,我们插入四条记录。
Uri returnUri=null; ContentResolver resolver=getContentResolver(); Uri uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON); ContentValues values=new ContentValues(); values.put(DBConst.COLUMN_NAME,"A"); values.put(DBConst.COLUMN_AGE,10); values.put(DBConst.COLUMN_SEX,"Male"); returnUri=resolver.insert(uri,values); if(returnUri!=null) Log.i(TAG, "return Uri = "+returnUri.toString()); values.put(DBConst.COLUMN_NAME,"B"); values.put(DBConst.COLUMN_AGE,11); values.put(DBConst.COLUMN_SEX,"Male"); returnUri=resolver.insert(uri,values); if(returnUri!=null) Log.i(TAG, "return Uri = "+returnUri.toString()); values.put(DBConst.COLUMN_NAME,"C"); values.put(DBConst.COLUMN_AGE,12); values.put(DBConst.COLUMN_SEX,"Female"); returnUri=resolver.insert(uri,values); if(returnUri!=null) Log.i(TAG, "return Uri = "+returnUri.toString()); values.put(DBConst.COLUMN_NAME,"D"); values.put(DBConst.COLUMN_AGE,13); values.put(DBConst.COLUMN_SEX,"Female"); returnUri=resolver.insert(uri,values); if(returnUri!=null) Log.i(TAG, "return Uri = "+returnUri.toString());
我们看一下输出结果。
I/com.breakloop.contentproviderdemo2.MainActivity: return Uri = content://com.breakloop.contentproviderdemo1/PERSON/1I/com.breakloop.contentproviderdemo2.MainActivity: return Uri = content://com.breakloop.contentproviderdemo1/PERSON/2I/com.breakloop.contentproviderdemo2.MainActivity: return Uri = content://com.breakloop.contentproviderdemo1/PERSON/3I/com.breakloop.contentproviderdemo2.MainActivity: return Uri = content://com.breakloop.contentproviderdemo1/PERSON/4
既然,写入成功,那我们查询一下。
public void selectRecord(){ Uri uri; String name="A"; int age=11; String sex="Male"; Log.i(TAG, "Select by Name"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByName/"+name); selectRecord(uri); Log.i(TAG, "Select by Age"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByAge/"+age); selectRecord(uri); Log.i(TAG, "Select by Sex"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/BySex/"+sex); selectRecord(uri); Log.i(TAG, "Select All"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON); selectRecord(uri); } private void selectRecord(Uri uri){ Cursor cursor; cursor=resolver.query(uri,new String[]{DBConst.COLUMN_AGE, DBConst.COLUMN_NAME, DBConst.COLUMN_SEX},null,null,null); if(cursor!=null){ while(cursor.moveToNext()){ Log.i(TAG, "name = "+cursor.getString(1)+ " age = "+cursor.getInt(0) +" "+cursor.getString(2)); } } }
输出结果如下
I/com.breakloop.contentproviderdemo2.MainActivity: Select by Name I/com.breakloop.contentproviderdemo2.MainActivity: name = A age = 10 Male I/com.breakloop.contentproviderdemo2.MainActivity: Select by Age I/com.breakloop.contentproviderdemo2.MainActivity: name = B age = 11 Male I/com.breakloop.contentproviderdemo2.MainActivity: Select by Sex I/com.breakloop.contentproviderdemo2.MainActivity: name = A age = 10 Male I/com.breakloop.contentproviderdemo2.MainActivity: name = B age = 11 Male I/com.breakloop.contentproviderdemo2.MainActivity: Select All I/com.breakloop.contentproviderdemo2.MainActivity: name = A age = 10 Male I/com.breakloop.contentproviderdemo2.MainActivity: name = B age = 11 Male I/com.breakloop.contentproviderdemo2.MainActivity: name = C age = 12 Female I/com.breakloop.contentproviderdemo2.MainActivity: name = D age = 13 Female
我们再来更新一下。
public void updateRecord(){ Uri uri; String name="A"; int age=11; String sex="Female"; ContentValues values=new ContentValues(); Log.i(TAG, "Update by Name"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByName/"+name); values.put(DBConst.COLUMN_NAME,name+name); update(uri,values); Log.i(TAG, "Update by Age"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByAge/"+age); values.clear(); values.put(DBConst.COLUMN_AGE,14); update(uri,values); Log.i(TAG, "Update by Sex"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/BySex/"+sex); values.clear(); values.put(DBConst.COLUMN_AGE,15); update(uri,values); Log.i(TAG, "Update All"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON); values.put(DBConst.COLUMN_SEX,"Male"); update(uri,values); Log.i(TAG, "Select All"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON); selectRecord(uri); } private void update(Uri uri,ContentValues values){ int count; count=resolver.update(uri,values,null,null); Log.i(TAG, "update "+count+" record"); }
结果如下
I/com.breakloop.contentproviderdemo2.MainActivity: Update by Name I/com.breakloop.contentproviderdemo2.MainActivity: update 1 record I/com.breakloop.contentproviderdemo2.MainActivity: Update by Age I/com.breakloop.contentproviderdemo2.MainActivity: update 1 record I/com.breakloop.contentproviderdemo2.MainActivity: Update by Sex I/com.breakloop.contentproviderdemo2.MainActivity: update 2 record I/com.breakloop.contentproviderdemo2.MainActivity: Update All I/com.breakloop.contentproviderdemo2.MainActivity: update 4 record I/com.breakloop.contentproviderdemo2.MainActivity: Select All I/com.breakloop.contentproviderdemo2.MainActivity: name = AA age = 15 Male I/com.breakloop.contentproviderdemo2.MainActivity: name = B age = 15 Male I/com.breakloop.contentproviderdemo2.MainActivity: name = C age = 15 Male I/com.breakloop.contentproviderdemo2.MainActivity: name = D age = 15 Male
都不要了,删除!(这里是对更新前的数据库进行的操作)
public void deleteRecord(){ Uri uri; String name="A"; int age=11; String sex="Female"; Log.i(TAG, "Delete by Name"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByName/"+name); delete(uri); Log.i(TAG, "Select All"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON); selectRecord(uri); Log.i(TAG, "Delete by Age"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByAge/"+age); delete(uri); Log.i(TAG, "Select All"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON); selectRecord(uri); Log.i(TAG, "Delete by Sex"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/BySex/"+sex); delete(uri); Log.i(TAG, "Select All"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON); selectRecord(uri); Log.i(TAG, "Delete All"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON); delete(uri); Log.i(TAG, "Select All"); uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON); selectRecord(uri); } private void delete(Uri uri){ int count; count=resolver.delete(uri,null,null); Log.i(TAG, "delete "+count+" record"); }
结果如下
I/com.breakloop.contentproviderdemo2.MainActivity: delete 1 record I/com.breakloop.contentproviderdemo2.MainActivity: Select All I/com.breakloop.contentproviderdemo2.MainActivity: name = B age = 11 Male I/com.breakloop.contentproviderdemo2.MainActivity: name = C age = 12 Female I/com.breakloop.contentproviderdemo2.MainActivity: name = D age = 13 Female I/com.breakloop.contentproviderdemo2.MainActivity: Delete by Age I/com.breakloop.contentproviderdemo2.MainActivity: delete 1 record I/com.breakloop.contentproviderdemo2.MainActivity: Select All I/com.breakloop.contentproviderdemo2.MainActivity: name = C age = 12 Female I/com.breakloop.contentproviderdemo2.MainActivity: name = D age = 13 Female I/com.breakloop.contentproviderdemo2.MainActivity: Delete by Sex I/com.breakloop.contentproviderdemo2.MainActivity: delete 2 record I/com.breakloop.contentproviderdemo2.MainActivity: Select All I/com.breakloop.contentproviderdemo2.MainActivity: Delete All I/com.breakloop.contentproviderdemo2.MainActivity: delete 0 record I/com.breakloop.contentproviderdemo2.MainActivity: Select All
至此,ContentProvider的创建和使用便介绍完了。
如果,只是为了使用,可以只看到这里就好了!
之后的部分,本文还未找到答案!因此,只是记录!
如果有知道的朋友,还望指点。
那么,还有什么?
还记得ContentProvider中的getType吗?
到目前为止,我们并没有实现getType
方法。而这并没有对ContentProvider
的使用造成任何影响。那么问题来了。getType
干嘛用的?什么时候用?
对于getType
的解释,网上有两种。
1. 用于对返回数据的解析。判断是一条数据,还是多条数据。若getType
方法被实现,则按照实现方法解析,提高了工作效率。否则,系统自动解析。
2. 避免new Intent(String action, Uri uri)方式无法启动activity。
对于解释1,并未从找到官方的出处。而对于解释2,不太明白!我用ContentProvider
碍activity
何事?
关于getType
方法的实现,其原型注释给出了答案。
方法返回的是MIME类型的String形式。根据Uri的结尾不同,输出也不同。
若Uri以Path结尾,则返回格式为
vnd.android.cursor.dir/vnd.
.
若Uri以id结尾,则返回格式为
vnd.android.cursor.item/vnd.
.
因此,我们实例中的getType()
实现为
@Override public String getType(Uri uri) { Log.i(TAG, "getType: "); String authorityAndPath=MatcherConst.AUTHORITY+"."+DBConst.TABLE_PERSON; String handerPath="vnd.android.cursor.dir/vnd."; String handerID="vnd.android.cursor.item/vnd."; switch (matcher.match(uri)){ case MatcherConst.BY_NONE: return handerPath+authorityAndPath; case MatcherConst.BY_AGE: case MatcherConst.BY_SEX: case MatcherConst.BY_NAME: return handerID+authorityAndPath; default: return null; } }
本文主要介绍了Android中四大组件的ContentProvider
的一些知识,觉得文章不错的喜欢的小伙伴可以关注加分享,也欢迎大家前来探讨交流。