一、相關ContentProvider概念解析:
1、ContentProvider簡介
在Android官方指出的Android的資料存儲方式總共有五種,分别是:Shared Preferences、網絡存儲、檔案存儲、外儲存儲、SQLite。但是我們知道一般這些存儲都隻是在單獨的一個應用程式之中達到一個資料的共享,有時候我們需要操作其他應用程式的一些資料,例如我們需要作業系統裡的媒體庫、通訊錄等,這時我們就可能通過ContentProvider來滿足我們的需求了。
2、為什麼要選擇ContentProvider?
ContentProvider向我們提供了我們在應用程式之前共享資料的一種機制,而我們知道每一個應用程式都是運作在不同的應用程式的,資料和檔案在不同應用程式之間達到資料的共享不是沒有可能,而是顯得比較複雜,而正好Android中的ContentProvider則達到了這一需求,比如有時候我們需要操作手機裡的聯系人,手機裡的多媒體等一些資訊,我們都可以用到這個ContentProvider來達到我們所需。
1)、ContentProvider為存儲和擷取資料提供了統一的接口。ContentProvide對資料進行封裝,不用關心資料存儲的細節。使用表的形式來組織資料。
2)、使用ContentProvider可以在不同的應用程式之間共享資料。
3)、Android為常見的一些資料提供了預設的ContentProvider(包括音頻、視訊、圖檔和通訊錄等)。
總的來說使用ContentProvider對外共享資料的好處是統一了資料的通路方式。
3、Uri介紹
為系統的每一個資源給其一個名字,比方說通話記錄。
1)、每一個ContentProvider都擁有一個公共的URI,這個URI用于表示這個ContentProvider所提供的資料。
2)、Android所提供的ContentProvider都存放在android.provider包中。 将其分為A,B,C,D 4個部分:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICdzFWRoRXdvN1LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX90TQNJTT65kMBRVT4FEVkZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DNzcjMxcDMyIDOyQDM0EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
A:标準字首,用來說明一個Content Provider控制這些資料,無法改變的;"content://"
B:URI 的辨別,用于唯一辨別這個ContentProvider,外部調用者可以根據這個辨別來找到它。它定義了是哪個Content Provider提供這些資料。對于第三方應用程式,為了保證URI辨別的唯一性,它必須是一個完整的、小寫的類名。這個辨別在 元素的 authorities屬性中說明:一般是定義該ContentProvider的包.類的名稱
C:路徑(path),通俗的講就是你要操作的資料庫中表的名字,或者你也可以自己定義,記得在使用的時候保持一緻就可以了;"content://com.bing.provider.myprovider/tablename"
D:如果URI中包含表示需要擷取的記錄的ID;則就傳回該id對應的資料,如果沒有ID,就表示傳回全部; "content://com.bing.provider.myprovider/tablename/#" #表示資料id。
PS:
路徑(path)可以用來表示我們要操作的資料,路徑的建構應根據業務而定,如下:
1、要操作person表中id為10的記錄,可以建構這樣的路徑:/person/10
2、要操作person表中id為10的記錄的name字段, person/10/name
3、要操作person表中的所有記錄,可以建構這樣的路徑:/person
4、要操作xxx表中的記錄,可以建構這樣的路徑:/xxx
5、當然要操作的資料不一定來自資料庫,也可以是檔案、xml或網絡等其他存儲方式,如下:
要操作xml檔案中person節點下的name節點,可以建構這樣的路徑:/person/name
6、如果要把一個字元串轉換成Uri,可以使用Uri類中的parse()方法,如下:Uri uri = Uri.parse("content://com.bing.provider.personprovider/person")
4、UriMatcher類使用介紹
因為Uri代表了要操作的資料,是以我們經常需要解析Uri,并從Uri中擷取資料。Android系統提供了兩個用于操作Uri的工具類,分别為UriMatcher和ContentUris
。掌握它們的使用,會便于我們的開發工作。
UriMatcher類用于比對Uri,它的用法如下:
首先第一步把你需要比對Uri路徑全部給注冊上,如下:
注冊完需要比對的Uri後,就可以使用sMatcher.match(uri)方法對輸入的Uri進行比對,如果比對就傳回比對碼,比對碼是調用addURI()方法傳入的第三個參數,假設比對content://com.ljq.provider.personprovider/person路徑,傳回的比對碼為1
5、ContentUris類使用介紹
ContentUris類用于操作Uri路徑後面的ID部分,它有兩個比較實用的方法:
withAppendedId(uri, id)用于為路徑加上ID部分:
parseId(uri)方法用于從路徑中擷取ID部分:
6、使用ContentProvider共享資料
1)ContentProvider類主要方法的作用:
public boolean onCreate():該方法在ContentProvider建立後就會被調用,Android開機後,ContentProvider在其它應用第一次通路它時才會被建立。
public Uri insert(Uri uri, ContentValues values):該方法用于供外部應用往ContentProvider添加資料。
public int delete(Uri uri, String selection, String[] selectionArgs):該方法用于供外部應用從ContentProvider删除資料。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):該方法用于供外部應用更新ContentProvider中的資料。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):該方法用于供外部應用從ContentProvider中擷取資料。
public String getType(Uri uri):該方法用于傳回目前Url所代表資料的MIME類型。
2)如果操作的資料屬于集合類型,那麼MIME類型字元串應該以vnd.android.cursor.dir/開頭,
例如:要得到所有person記錄的Uri為content://com.bing.provider.personprovider/person,那麼傳回的MIME類型字元串應該為:"vnd.android.cursor.dir/person"。
3)如果要操作的資料屬于非集合類型資料,那麼MIME類型字元串應該以vnd.android.cursor.item/開頭,
例如:得到id為10的person記錄,Uri為content://com.bing.provider.personprovider/person/10,那麼傳回的MIME類型字元串為:"vnd.android.cursor.item/person"。
7、ContentResolver操作ContentProvider中的資料
1)當外部應用需要對ContentProvider中的資料進行添加、删除、修改和查詢操作時,可以使用ContentResolver
類來完成,要擷取ContentResolver 對象,可以使用Activity提供的getContentResolver()方法。
2)ContentResolver
類提供了與ContentProvider類相同簽名的四個方法:
public Uri insert(Uri uri, ContentValues values):該方法用于往ContentProvider添加資料。
public int delete(Uri uri, String selection, String[] selectionArgs):該方法用于從ContentProvider删除資料。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):該方法用于更新ContentProvider中的資料。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):該方法用于從ContentProvider中擷取資料。
這些方法的第一個參數為Uri,代表要操作的ContentProvider和對其中的什麼資料進行操作,
其實和contentprovider裡面的方法是一樣的.他們所對應的資料,最終是會被傳到我們在之前程式裡面定義的那個contentprovider類的方法,
假設給定的是:Uri.parse("content://com.bing.providers.personprovider/person/10"),那麼将會對主機名為com.bing.providers.personprovider的ContentProvider進行操作,操作的資料為person表中id為10的記錄。
使用ContentResolver對ContentProvider中的資料進行添加、删除、修改和查詢操作:
8、監聽ContentProvider中資料的變化
如果ContentProvider的通路者需要知道ContentProvider中的資料發生變化,可以在ContentProvider發生資料變化時調用getContentResolver().notifyChange(uri, null)來通知注冊在此URI上的通路者,例子如下:
如果ContentProvider的通路者需要得到資料變化通知,必須使用ContentObserver對資料(資料采用uri描述)進行監聽,當監聽到資料變化通知時,系統就會調用ContentObserver的onChange()方法:
二、ContentProvider的實作過程
1、定義一個CONTENT_URI常量,提供了通路ContentProvider的辨別符。
其中:content是協定
Com.exmaple.codelab.transportationprovider是類名,包含完整的包名。
Uri.parse将一個字元串轉換成Uri類型。
如果Provider包含子表,同樣定義包含字表的CONTENT_URI。
content://com.example.codelab.transportationprovider/train
content://com.example.codelab.transportationprovider/air/domestic
content://com.example.codelab.transportationprovider/air/international
然後定義列,確定裡面包含一個_id的列。
2、定義一個類,繼承ContentProvider。
先介紹一下ContentProvider用到的UriMatcher。UriMatcher的一個重要的函數是match(Uri uri)。這個函數可以比對Uri,根據傳入的不同Uri傳回不同的自定義整形值,以表明Uri通路的不同資源的類型。
例如:
這裡UriMatcher類型的靜态字段是用來比對傳入到ContentProvider中的Uri的類。其構造方法傳入的比對碼是使用match()方法比對根路徑時傳回的值,這個比對碼可以為一個大于零的數表示比對根路徑或傳入-1,即常量UriMatcher.NO_MATCH表示不比對根路徑。
addURI()方法是用來增加其他URI比對路徑的,
第一個參數傳入辨別ContentProvider的AUTHORITY字元串。
第二個參數傳入需要比對的路徑,這裡的#号為通配符,代表比對任意數字,另外還可以用*來比對任意文本。
第三個參數必須傳入一個大于零的比對碼,用于match()方法對相比對的URI傳回相對應的比對碼。 例如:sMatcher.addURI(“com.test.provider.personprovider”,
“person”, 1);如果match()方法比對content://com.test.provider.personprovider/person路徑,傳回比對碼為1。
3、實作query,insert,update,delete,getType和onCreate方法。
4、在AndroidManifest.xml當中進行聲明。
三、執行個體
1、常量類
PS:
在建立UriMatcher對象uriMatcher時,我們傳給構造函數的參數為UriMatcher.NO_MATCH,它表示當uriMatcher不能比對指定的URI時,就傳回代碼UriMatcher.NO_MATCH。接下來增加了三個比對規則,分别是uriMatcher
= new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(ContentData.AUTHORITY, "teacher", TEACHERS); uriMatcher.addURI(ContentData.AUTHORITY,
"teacher/#", TEACHER);
它們的比對碼分别是teacher.ITEM、teacher.ITEM_ID和teacher.ITEM_POS,其中,符号#表示比對任何數字。
2、SQLite操作類DBOpenHelper
3、内容提供者代碼
1、這裡我們在ArticlesProvider類的内部中定義了一個DBHelper類,它繼承于SQLiteOpenHelper類,它用是用輔助我們操作資料庫的。使用這個DBHelper類來輔助操作資料庫的好處是隻有當我們第一次對資料庫時行操作時,系統才會執行打開資料庫檔案的操作。拿我們這個例子來說,隻有第三方應用程式第一次調用query、insert、update或者delete函數來操作資料庫時,我們才會真正去打開相應的資料庫檔案。這樣在onCreate函數裡,就不用執行打開資料庫的操作,因為這是一個耗時的操作,而在onCreate函數中,要避免執行這些耗時的操作。
2、我們在實作自己的Content Provider時,必須繼承于ContentProvider類,并且實作以下六個函數:
-- onCreate(),用來執行一些初始化的工作。
-- query(Uri, String[], String, String[], String),用來傳回資料給調用者。
-- insert(Uri, ContentValues),用來插入新的資料。
-- update(Uri, ContentValues, String, String[]),用來更新已有的資料。
-- delete(Uri, String, String[]),用來删除資料。
-- getType(Uri),用來傳回資料的MIME類型。
4、manifest
在配置Content Provider的時候,最重要的就是要指定它的authorities屬性了,隻有配置了這個屬性,第三方應用程式才能通過它來找到這個Content Provider。這要需要注意的,這裡配置的authorities屬性的值是和我們前面在Articles.java檔案中定義的AUTHORITY常量的值是一緻的。另外一個屬性multiprocess是一個布爾值,它表示這個Content Provider是否可以在每個客戶程序中建立一個執行個體,這樣做的目的是為了減少程序間通信的開銷。這裡我們為了減少不必要的記憶體開銷,把屬性multiprocess的值設定為false,使得系統隻能有一個Content
Provider執行個體存在,它運作在自己的程序中。在這個配置檔案裡面,我們還可以設定這個Content Provider的通路權限,這裡我們為了簡單起見,就不設定權限了。
6、布局檔案
7、activity
最終的效果如下:
PS:Content Provider中的資料更新通知機制
Android應用程式元件Content Provider中的資料更新通知機制和Android系統中的廣播(Broadcast)通知機制的實作思路是相似的。
在Android的廣播機制中,首先是接收者對自己感興趣的廣播進行注冊,接着當發送者發出這些廣播時,接收者就會得到通知了。更多關于Android系統的廣播機制的知識,可以參考前面這一文章。
然而,Content Provider中的資料監控機制與Android系統中的廣播機制又有三個主要的差別,
一是前者是通過URI來把通知的發送者和接收者關聯在一起的,而後者是通過Intent來關聯的,
二是前者的通知注冊中心是由ContentService服務來扮演的,而後者是由ActivityManagerService服務來扮演的,
三是前者負責接收資料更新通知的類必須要繼承ContentObserver類,而後者要繼承BroadcastReceiver類。
之是以會有這些差別,是由于Content Proivder元件的資料共享功能本身就是建立在URI的基礎之上的,是以專門針對URI來設計另外一套通知機制會更實用和友善,而Android系統的廣播機制是一種更加通用的事件通知機制,它的适用範圍會更廣泛一些。