嗨,你終於來啦 ~ 等你很久啦~ 喜歡的小夥伴歡迎關注,我會按期分享Android知識點及解析,還會不斷更新的BATJ面試專題,歡迎你們前來探討交流,若有好的文章也歡迎投稿。javascript
上週我給你們介紹了一下 Android 四大組件之一BroadcastReceiver
,你們是否有所收穫呢?有些小夥伴催我更新剩下的,今天我就來給你們介紹一下ContentProvider
。java
ContentProvider,是自身APP開放給第三方APP的,用於訪問自身數據庫數據的接口。
第三方APP能夠經過該接口,對指定的數據進行增刪改查。android
那麼如何定義自身的ContentProvider
接口呢?面試
在回答問題以前,先來關注一下Uri。sql
緣由在於,uri是ContentProvider解析外部請求(或者說是,第三方訪問自身數據庫)的關鍵參數。數據庫
Uri的字符串格式以下app
例如工具
從上方Uri示例中,能夠獲取到如下信息。
第三方APP想要
com.breakloop.sqlitehelperdemo
的數據庫。至於哪一個,由contentProvider
內部映射指定。contentProvider
說了算。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/1
I/com.breakloop.contentproviderdemo2.MainActivity: return Uri = content://com.breakloop.contentproviderdemo1/PERSON/2
I/com.breakloop.contentproviderdemo2.MainActivity: return Uri = content://com.breakloop.contentproviderdemo1/PERSON/3
I/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.<authority>.<path>
若Uri以id結尾,則返回格式爲
vnd.android.cursor.item/vnd.<authority>.<path>
所以,咱們實例中的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;
}
}
複製代碼
ContentProvider
的一些知識,以爲文章不錯的喜歡的小夥伴能夠關注加分享,也歡迎你們前來探討交流。