ContentResolver與ContentProvider的搭配使用

簡介

Android中, ContentResolver是通過Uri來獲取對應數據的數據, 而提供數據的,通常就是ContentProvider。ContentProvider可以作爲一個公共的數據源,爲多個app提供數據的增刪改查。

以下例子先建立一個ContentProvider, 在其內部簡歷數據庫,然後讓ContentResolver來查詢數據。

先看例子的示意圖:
1、插入數據和查詢數據
查看數據庫的兩列數據:1、單詞; 2、單詞描述, 這裏直插入了單詞,暫未插入描述
在這裏插入圖片描述
2、修改數據
修改以b結尾的單詞的描述, 這裏把描述改爲了222

在這裏插入圖片描述

3、刪除數據
刪除以a開頭的所有單詞
在這裏插入圖片描述
4、數據庫表(用Sqlite Expert工具查看)
主要有三列: id, word, describe
在這裏插入圖片描述
Sqlite Expert工具破解版下載地址: https://download.csdn.net/download/qq475703980/11833576

一、ContentProvider的創建

1、自定義ContentProvider

繼承ContentProvider後,需要實現六個抽象方法:

public class DictProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
		//只會在ContentProvider啓動時調用一次
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}

下面就介紹具體這幾個方法如何使用。

Uri簡介

Uri: 統一資源標識符(Uniform Resource Identifier,URI)。每個資源都應該有唯一一個符號來標識,就像每一個網址都有唯一可識別的地址一樣。

我們先看當前博客的網址:https://blog.csdn.net/qq475703980

這個地址可以分爲三部分:

  • 1、https:// :這個表示協議,只能通過https協議來訪問,這部分是固定的
  • 2、blog.csdn.net : 域名部分,這是CSDN的網址
  • 3、qq475703980 :網址資源部分,這裏就指向了CSDN的qq475703980這個博客

在android中,我們provider的uri地址通常格式爲:“content://com.op.billy.dict.provider/dict”, 其中“content://”是固定的協議,而"com.op.billy.dict.provider"是一個provider的唯一標識,這個也需要在manifest中註冊 "dict"標識這個provider裏面的某個資源路徑。

在Manifest中註冊ContentProvider

<provider
    android:name=".DictProvider"
    android:authorities="com.op.billy.dict.provider"
    android:exported="true" />

authorities是用來一個Provider的唯一標識,就類似於網站的網址一樣,每個網站的網址是固定的,不會兩家公司的網址都是相同的,所以在Android中,爲了防止兩個應用的authorities相同, 如果後安裝的apk的authorities和已安裝的apk的authorities相同,則新的apk無法安裝成功。因爲apk安裝時,會首先解析apk的manifest,然後將其中的四大組件註冊到系統服務中,進行統一管理,先安裝的apk自然就先註冊了。

1、onCreate方法

onCreate方法只會在provider啓動時調用一次,啓動後不再回調此方法,通常用來做一些初始化操作及拿到DatabaseHelper的引用:

@Override
public boolean onCreate() {
    Log.d(TAG, "onCreate");
	//初始化操作dict_db這個數據庫的DatabaseHelper,自定義的DictDatabaseHelper主要是用來創建了數據庫dict_db
    mDbHelper = new DictDatabaseHelper(getContext(), "dict_db", null, 1);
    return true;
}

2、getType方法

getType主要是用來返回數據mimetype類型:

@Override
public String getType(@NonNull Uri uri) {
    //返回指定Uri參數對應的數據的MIME TYPE 類型
    switch (mUriMatcher.match(uri)) {
        case CODE_DICT:
			//返回的cursor是單項記錄,即只查詢了一行
            return "vnd.android.cursor.item/op.dict";
        case CODE_DESCRIBE:
            return "vnd.android.cursor.item/op.describe";
        case CODE_DICTS:
			//返回的cursor是多想記錄,查詢了多行
            return "vnd.android.cursor.dir/op.dicts";
        case CODE_DESCRIBES:
            return "vnd.android.cursor.dir/op.describes";
    }
    return null;
}

這個方法的意義是返回某種mime類型,確定了類型以後,去查詢數據庫的時候就不會再進行判斷了, 效率更高;如果這裏返回任意字符串也是可以的,只是這個方法就是去了作用,查詢數據庫的效率會降低。

這裏根據傳遞進來的uri,返回不同的mime類型, 其中"vnd.android.cursor.item/op.dict"表示cursor操作單行記錄錄,"vnd.android.cursor.dir/op.dicts"表示 cursor操作多行記錄。

其中前半段"vnd.android.cursor.dir"是Android系統定義的mime類型,是系統可以識別的,“vnd.android.cursor.dir” 表示cursor可以操作的是多行數據; 後半段"/op.dicts"使我們自定義的字段,表示具體的我們的provider內部使用的一種類型。

系統定義了很多mime類型,我們在manifest清單中設置四大組件有時也會使用到, 具體的mime類型介紹可以參考博客: https://blog.csdn.net/harvic880925/article/details/44620851

3、insert方法

@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
    SQLiteDatabase db = mDbHelper.getWritableDatabase();
    switch (mUriMatcher.match(uri)) {
        case CODE_DICT:
            long rowId = db.insert(DictDatabaseHelper.TABLE_DICT, DictDatabaseHelper.COLUMN_WORD, values);
            if(rowId > 0) {
                Log.d(TAG, "rowId = "+rowId);
                Uri wordUri = ContentUris.withAppendedId(uri, rowId);
				return  wordUri;//返回這個插入單詞的位置
                notifyDataChanged(uri);
            }
    }
    return null;
}

這裏主要介紹參數ContentValues, ContentValue主要通過key ,value來保存值,其key應該與數據庫表中的列名相同, value就是這一列需要插入的值, 如果key與列名不對應,會報錯。

4、query方法

執行數據庫的查詢方法,

@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
    Log.d(TAG, "query");
    SQLiteDatabase db = mDbHelper.getReadableDatabase();
    switch (mUriMatcher.match(uri)) {
        case CODE_DICT:
            Log.d(TAG, "CODE_DICT");
            //執行查詢
            return db.query(DictDatabaseHelper.TABLE_DICT, projection, selection, selectionArgs,   null, null, sortOrder);
        default:
    }
    return null;
}

它的參數分別爲:

  • Uri uri: 根據傳遞過來的uri, 我們去查詢數據庫中不同的表,uri和表的對應關係,使我們自己定義的;
  • String[] projection: 要查詢的列的名字 組成的數組,如果有數據,返回的cursor數據也會按照這個projection傳遞進來的列名的順序返回對應的數據;
  • String selection: 篩選條件,如果需要對數據進行某種篩選
  • String[] selectionArgs: 如果上一條 selection是一個完整的sql篩選語句,則此參數可以爲null, 如果不是一個完整的selection語句,其具體參數用佔位符?替代了,那麼這個參數依次傳遞的數據替代前一句的佔位符?;
  • String sortOrder:數據庫的排序語句, 可以爲null;

剩下的delete和update方法就和上述方法類似了,傳入的參數已經介紹過了, 下面看ContentResolver如何來操作我們的Provider,使其可以增刪改查數據庫的數據。

二、使用ContentResolver操作ContentProvider的數據

只要有Context上下文,我們就可以通過它來獲取ContentResolver

ContentResolver mResolver = context.getContentResolver();

1、使用ContentResolver插入數據

private final String AUTHORITY = "com.op.billy.dict.provider";
private final String DICT_PATH = "dict";
private final String COLUMN_NAME_DICT = "word";
private final String COLUMN_NAME_DESCRIBE = "describe";
private final Uri URI_DICT = Uri.parse("content://" + AUTHORITY + File.separatorChar + DICT_PATH);
...
ContentValues cv = new ContentValues();
cv.put(COLUMN_NAME_DICT, input);//根據列名插入數據
mResolver.insert(URI_DICT, cv);

使用ContentResolver修改更新數據

String input_describe = mEditText.getText().toString();
 cv.put(COLUMN_NAME_DESCRIBE,input_describe);

String where = "word like ?";//"word like '%b'";//如果使用?做佔位符,那麼下面再傳入模糊查詢的字符串時,不在需要單引號
 String[] selectionArgs_update = {"%b"};
  mResolver.update(URI_DICT_DESCRIBE, cv, where, selectionArgs_update);

注意坑點, 這裏"word like ‘%b’"表示篩選的sql語句,指篩選word這一列中以b結尾的單詞,注意如果直接在where語句中這麼寫, %b 要加單引號, 如果使用下一種佔位符的方式傳遞,則不需要單引號:

 String where = "word like ?"
 String[] selectionArgs_update = {"%b"};

###使用ContentResolver修改獲取數據

//projection :想要查詢的列的的集合(傳入列名),可以使單列或多列
String selection = "word like '%b'"; //查詢的選擇條件, 具體值用佔位符 ? 表示
 //查詢條件中的具體值,代替上述的佔位符, a%表示以a開頭, %b表示以b結尾, %c%表示包含c
String[] selectionArgs =  {" a% ", " %b "};
  //查詢單詞以a開頭的,描述以b結尾的 行
Cursor cursor = mResolver.query(URI_DICT, mProjections, null, null, null);
if(cursor != null &&cursor.getCount() > 0) {
int count = cursor.getCount();
String result = "result count= " + count +"\n";
while(cursor.moveToNext()) {
     result += cursor.getString(0) +", " +cursor.getString(1)+"\n";
}
mTvShow.setText(result);

##demo 源碼
以上就是關於ContentProvider以及ContentResolver的使用, demo的源碼地址如下,環宇查看:

https://github.com/Billyshi/BillyApplication.git

https://github.com/Billyshi/BillyProvider.git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章