還沒弄懂四大組件?帶你全面解析ContentProvider篇

嗨,你終於來啦 ~ 等你很久啦~ 喜歡的小夥伴歡迎關注,我會按期分享Android知識點及解析,還會不斷更新的BATJ面試專題,歡迎你們前來探討交流,若有好的文章也歡迎投稿。javascript

前言

上週我給你們介紹了一下 Android 四大組件之一BroadcastReceiver,你們是否有所收穫呢?有些小夥伴催我更新剩下的,今天我就來給你們介紹一下ContentProviderjava

1.什麼是ContentProvider

ContentProvider,是自身APP開放給第三方APP的,用於訪問自身數據庫數據的接口。
第三方APP能夠經過該接口,對指定的數據進行增刪改查。android

那麼如何定義自身的ContentProvider接口呢?面試

在回答問題以前,先來關注一下Uri。sql

2.什麼是Uri

緣由在於,uri是ContentProvider解析外部請求(或者說是,第三方訪問自身數據庫)的關鍵參數。數據庫

Uri的字符串格式以下app

content://package/table/idide

例如工具

content://com.breakloop.sqlitehelperdemo/hero/1oop

從上方Uri示例中,能夠獲取到如下信息。

第三方APP想要

  1. 訪問com.breakloop.sqlitehelperdemo的數據庫。至於哪一個,由contentProvider內部映射指定。
  2. 訪問表hero。至於表名是否是真爲hero,也由contentProvider說了算。
  3. 訪問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/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,不太明白!我用ContentProvideractivity何事?

關於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;
        }
    }
複製代碼

8.總結

  • 本文主要介紹了Android中四大組件的ContentProvider的一些知識,以爲文章不錯的喜歡的小夥伴能夠關注加分享,也歡迎你們前來探討交流。
相關文章
相關標籤/搜索