我要做 Android 之 ContentProvider

相信大家對于 Android 頂頂大名的四大組件也不陌生了。今天我們來談一談 ContentProvider 的用法。什么東西都是基本最重要啊,可以人是一個健忘的動物,所以需要做下來筆記,常看常新。愿我們成為真實的自己。

ContentProvider概要

從系統提供的Provider訪問數據

  • 內容URI的組成
  • ContentResolve類

創建自己的Provider

  • UriMater類
  • 自定義一個Provider的步驟

ContentProvider 也有存儲數據的功能,但是與安卓自帶的數據庫和 SharedPreferences 以及文件存儲方法不同的是,后者保存下的數據只能被該應用程序使用,而前者可以讓不同應用程序之間進行數據共享,它還可以選擇只對哪一部分數據進行共享,從而保證程序中的隱私數據不會有泄漏風險。所以組件ContentProvider主要負責存儲和共享數據。

ContentProvider有兩種形式:可以使用現有的內容提供者來讀取和操作相應程序中的數據,也可以創建自己的內容提供者給這個程序的數據提供外部訪問接口。

2.從系統提供的Provider訪問數據

既然ContentProvider有對外共享數據的功能,換句話說,其他應用程序可以通過ContentProvider對應用中的數據進行增刪改查,之前學習SQLite數據存儲的時候就提到過可以實現增刪改查的各種輔助性方法,實際上ContentProvider是對SQLiteOpenHelper的進一步封裝,不過不再用單純的表名指明被操作的表,畢竟現在是其他程序訪問它,而是用有一定格式規范的內容URI來代替。下面先來學習URI的組成。

(1)URI的組成

以之前學習數據庫的demo為例,它的包名是com.example.myapplication,如果其他程序想訪問該程序student.db中的student表,那么需要的內容URI如圖所示:

01.png

可以看出內容 URI 可以非常清楚地表達出我們想要訪問哪個程序中哪張表里的數據,但還沒完,還需要將它解析成 Uri 對象才可以作為參數傳入。通過調用 Uri.parse()方法,就可以將內容 URI 字符串解析成 Uri 對象了,代碼如下:

02.png

Uri 這個類就是專門做這種鏈接轉換的。其實這種鏈接方式和 http 很像。content:是不能變化的,后面就是包名.provider ,也是一定的,最后就是表名。

(2)ContentResolve類

現在有了酷似“表名”的Uri,類似的,在ContentResolver類中提供的一系列用于對數據進行增刪改查操作的方法也酷似SQLiteDatabase的那些輔助性方法:insert()方法用于添加數據,update()方法用于更新數據,delete()方法用于刪除數據,query()方法用于查詢數據。它們不僅方法名一樣,連提供的參數都非常相似,見下圖,紅色部分是區別:


03.png

所以其他程序若想要訪問ContentProvider中共享的數據的方法是:

第一:通過 Context 中的 getContentResolver() 方法實例化一個 ContentResolver 對象。

第二:調用該對象的增刪改查方法去操作 ContentProvider 中的數據。

下面我們來看看查詢聯系人數據的基本代碼:

04.png

別忘記在注冊文件中聲明權限

不但如此,Android 6.0 之后有了運行時權限,所以我們還要加上判斷才可以。

05.png

這一部分是通用代碼,大家可以在使用的過程中把這部分代碼封裝到 BaseActivity 中去。這樣可以增加代碼復用。


06.png

這是需要動態申請權限的圖標,不過沒關系,大家記不得可以去查找一下即可。

3.創建自己的Provider

(1)UriMater類

UriMater 類有匹配內容 URI 的功能,在這里常用它的兩個方法:一個是 addURI() 方法來傳入 URI,它接收三個參數(權限,路徑,一個自定義代碼);另一個是 match() 方法用來匹配 URI,接收一個 Uri 對象,返回值是某個能夠匹配這個 Uri 對象所對應的自定義代碼,利用這個自定義代碼,就可以判斷出調用方期望訪問的是哪張表中的數據了。

(2)自定義一個Provider的步驟

步驟一:新建一個類去繼承ContentProvider。
步驟二:重寫ContentProvider的六個抽象方法,方法及含義如圖:

public class MyProvider extends ContentProvider {
    // 在ContentProvider 創建后調用
    @Override
    public boolean onCreate() {
        return false;
    }
    //該方法用于提供外部應用從 ContentProvider 中獲取數據
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }
    @Nullable
    @Override   //用于返回當前 Uri 代表的 MIME 類型
    public String getType(@NonNull Uri uri) {
        return null;
    }
    //該方法用于提供外部應用從 ContentProvider 中插入數據
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }
    //該方法用于提供外部應用從 ContentProvider 中刪除數據
    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
    //該方法用于提供外部應用從 ContentProvider 中修改數據
    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}

步驟三:在配置文件中進行注冊,并注明屬性:

android:authorities即Provider的權限,形式是包名.provider
android:name即Provider的全名,形式是包名.類名
android:exported="true"指明該Provider可被其它程序訪問。

(3)例子:為student.db創建MyProvider

接下里還是給上篇的數據庫demo創建一個自定義提供器MyProvider,然后在別的應用程序中通過MyProvider去操作student.db中的數據。

開始自定義提供器!一開始定義了四個常量,分別表示訪問student表中的所有數據、訪問student表中的單條數據(student/#用于表示student表中任意一行記錄)、訪問course表中的所有數據和訪問course表中的單條數據。然后在靜態代碼塊里對UriMatcher進行了初始化操作,將期望匹配的幾種URI格式添加了進去。

07.png

接下來就是六個抽象方法的具體實現了,先看onCreate()方法,這里創建了一個MyHelper的實例,然后返回true表示內容提供器初始化成功,現在數據庫就已經完成了創建或升級操作。


08.png

接下來是 getType()方法,需要返回一個MIME字符串。一個內容URI所對應的MIME字符串主要由三部分組分,Android對這三個部分做了以下格式規定:必須以vnd開頭;如果內容URI以路徑結尾,則后接android.cursor.dir/,如果內容URI以id結尾,則后接android.cursor.item/;最后接上vnd.< authority>.< path>。所以四個內容URI對應的MIME字符串分別是:

09.png

在query()方法里先獲取到SQLiteDatabase的實例,然后根據傳入的Uri參數判斷出用戶想要訪問哪張表,再調用SQLiteDatabase的query()進行查詢并將Cursor對象返回就好了。注意當訪問的是單條數據時調用了Uri對象的getPathSegments()方法,它會將內容URI權限之后的部分以“/”符號進行分割,并把分割后的結果放入到一個字符串列表中,那這個列表的第0個位置存放的就是路徑,第1個位置存放的就是id了。得到了id之后,再通過selection和selectionArgs參數進行約束,就實現了查詢單條數據的功能。

補充部分:事務有關:
事務是恢復和并發控制的基本單位。

事務應該具有4個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱為ACID特性

原子性(atomicity)。一個事務是一個不可分割的工作單位,事務中包括的諸操作要么都做,要么都不做。

一致性(consistency)。事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。

隔離性(isolation)。一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對并發的其他事務是隔離的,并發執行的各個事務之間不能互相干擾。

持久性(durability)。持續性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。


/**
     * SQLite的事務管理
     */
    public void transation() {
        SQLiteDatabase db = helper.getWritableDatabase();
        // 事務開始
        db.beginTransaction();
        //事務的處理
        try {
            db.execSQL("update person set age = age-1 where name = 'xiaohong'");
            db.execSQL("update person set age = age+1 where name = 'xiaolan'");
            //事務處理成功
            db.setTransactionSuccessful();
        } finally {
            //結束事務
            db.endTransaction();
        }
 
    }

當使用 sqlite 數據庫批量插入數據的時候使用事務就可以提升效率。

在使用 sqlite 數據庫的時候有那些可以優化的地方。

一:顯示使用事務
Android中,無論是使用SQLiteDatabase的insert,delete等方法還是execSQL都開啟了事務,來確保每一次操作都具有原子性,使得結果要么是操作之后的正確結果,要么是操作之前的結果。所以我們可以顯示的支持事務,這樣事務的操作打開次數會明顯變少。

二:建立索引
創建索引的基本語法:

CREATE INDEX index_name ON table_name;

三:及時關閉Cursor

四:耗時異步化
數據庫的操作,屬于本地IO,通常比較耗時,如果處理不好,很容易導致ANR,因此建議將這些耗時操作放入異步線程中處理。

今天狀態不夠好,前途漫漫不知路在何方。只能一步一步向前走。
愿我們成為真實的自己。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容