喜欢的小伙伴欢迎关注,我会定期分享Android知识点及解析,还会不断更新的BATJ面试专题,欢迎大家前来探讨交流,如有好的文章也欢迎投稿。
1.什么是ContentProvider
ContentProvider,是自身APP开放给第三方APP的,用于访问自身数据库数据的接口。
第三方APP可以通过该接口,对指定的数据进行增删改查。
那么如何定义自身的ContentProvider
接口呢?
在回答问题之前,先来关注一下Uri。
2.什么是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
工具类的引入。
3.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
的创建。
4.ContentProvider的创建
先用Android Studio 创建一个ContentProvider
.
创建过程中,需要提供AUTHORITY,
ContentProvider
生成后,Android Studio将自动帮助ContentProvider
在Manifest中进行注册。
接下来,我们看看ContentProvider
的结构。
5.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
数据库对象的存在,进而可以对数据库进行相应操作。
6.行为代码Mapping数据库操作
我们先看一下最简单的插入操作。
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呢?
7.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; } }
8.总结
本文主要介绍了Android中四大组件的
ContentProvider
的一些知识,觉得文章不错的喜欢的小伙伴可以关注加分享,也欢迎大家前来探讨交流。