轉載請注明出處:[http://blog.csdn.net/dmk877/article/details/50387741](http://blog.csdn.net/dmk877/article/details/50387741)
苦心人天不負臥薪嘗膽三千越甲可吞吳,有志者天不負釜底抽薪百二秦川終屬楚。這是一對非常勵志的名言,每當讀這句話都會被震撼一下,然后接著頹廢,哈哈,最近的工作比較忙,也在這里提醒自己,一定要堅持下去,一定要堅持一件對自己有益的事情。
裝逼到此進入正題,今天要討論的主要內容是ContentProvider(內容提供者),ContentProvider也是android的四大組件之一,可見其在android中的重要性,可能大家用ContentProvider比其他三個組件用的少一點,但是ContentProvider同樣的非常重要,有的人知道怎么使用ContentProvider,但是對于ContentProvider的原理等,并沒有搞清楚,沒關系,通過本篇博客相信你會對ContentProvider有一個全新的認識。
通過本篇博客你將學到以下知識
①什么是內容提供者
②為什么會有內容提供者
③怎樣使用內容提供者
④ContentProvider中的Uri的詳細介紹
⑤ContentResolver講解
⑥UriMatch用法介紹
⑦ContentObserver用法詳解
⑧通過一個案例來講解自定義ContentProvider的執行過程(下一篇將給大家帶來調用系統的ContentProvider)
**1.什么是內容提供者?**
首先我們必須要明白的是ContentProvider(內容提供者)是android中的四大組件之一,但是在一般的開發中,可能使用比較少。ContentProvider為不同的軟件之間數據共享,提供統一的接口。而且ContentProvider是以類似數據庫中表的方式將數據暴露,也就是說ContentProvider就像一個“數據庫”。那么外界獲取其提供的數據,也就應該與從數據庫中獲取數據的操作基本一樣,只不過是采用URI來表示外界需要訪問的“數據庫”。至于如何從URI中識別出外界需要的是哪個“數據庫”這就是Android底層需要做的事情了,也就是說,如果我們想讓其他的應用使用我們自己程序內的數據,就可以使用ContentProvider定義一個對外開放的接口,從而使得其他的應用可以使用我們自己應用中的文件、數據庫內存儲的信息。當然,自己開發的應用需要給其他應用共享信息的需求可能比較少見,但是在Android系統中,很多數據如:聯系人信息、短信信息、圖片庫、音頻庫等,這些信息在開發中還是經常用到的,這些信息谷歌工程師已經幫我們封裝好了,我們可以使用谷歌給我的Uri去直接訪問這些數據。所以對于ContentProvider我們還是需要認真的學習的,在遇到獲取聯系人信息,圖片庫,音視頻庫等需求的時候,才能更好的實現功能。
**2.為什么會有內容提供者?**
當應用繼承ContentProvider類,并重寫該類用于提供數據和存儲數據的方法,就可以向其他應用共享其數據。雖然使用其他方法也可以對外共享數據,但數據訪問方式會因數據存儲的方式而不同,如:采用文件方式對外共享數據,需要進行文件操作讀寫數據;采用sharedpreferences共享數據,需要使用sharedpreferences API讀寫數據。而使用ContentProvider共享數據的好處是統一了數據訪問方式,這也是為什么會有內容提供者的原因。
**3.怎么使用內容提供者?**
在理解了什么是內容提供者,為什么會有內容提供者之后,我想在大家腦海中的浮現的一個問題就是怎么使用內容提供者,這也是今天我們要討論的重點內容。在前面的博客我也說到學習這種東西的最好方法是看谷歌給出的官方文檔,那么好我們先來翻譯一段谷歌給出的介紹(注:這是本地的文檔,我采用的是脫機工作地址(file:///D:/adt-bundle-windows-x86_64_20140101/sdk/docs/reference/android/content/ContentProvider.html))。
翻譯:
內容提供者是android應用程序的基本構建塊之一,它們封裝數據并將封裝的數據通過單一的ContentResolver接口提供給應用程序。當你需要在多個應用之間共享數據的時候就需要用到內容提供者。例如,手機中的聯系人數據會被多個應用所用到所以必須要用內容提供者存儲起來。如果你不需要在多個應用之間共享數據,你可以使用一個數據庫,直接通過SQLite數據庫。?當通過content?resolver發送一個請求時,系統會檢查給定的URI并把請求傳給有注冊授權的Contentprovider。?UriMatcher類有助于解析uri。
需要實現的主要方法是:
public boolean?onCreate() 在創建ContentProvider時調用
public Cursor?query(Uri, String[], String, String[], String) 用于查詢指定Uri的ContentProvider,返回一個Cursor
public Uri?insert(Uri, ContentValues) 用于添加數據到指定Uri的ContentProvider中,(外部應用向ContentProvider中添加數據)
public int?update(Uri, ContentValues, String, String[]) 用于更新指定Uri的ContentProvider中的數據
public int?delete(Uri, String, String[]) 用于從指定Uri的ContentProvider中刪除數據
public String?getType(Uri) 用于返回指定的Uri中的數據的MIME類型
數據訪問的方法(如:insert(Uri, ContentValues) and update(Uri, ContentValues, String, String[]))可能被多個線程同時調用,此時必須是線程安全的。其他方法(如: onCreate())只能被應用的主線程調用,它應當避免冗長的操作。ContentResolver(內容解析者)請求被自動轉發到合適的內容提供者實例,所以子類不需要擔心跨進程調用的細節。
唉,每次最頭疼的就是看全英文的文檔,發現自己的英語水平太差,最近也在空閑時間學習學習英語,但是對于英語從來就沒感興趣過,希望自己可以堅持一段時間吧,其實在開發過程中遇到那些比較難解決的問題,在國外的有名的網站中基本都是可以查找到的,但是對于我這樣一個英語水平差的人來說,說多了其實都是眼淚。。。,我先哭一會。。。
關于怎樣使用ContentProvider后面有實例幫助大家理解。
**4.Uri詳解**
在上面的翻譯中如果你認真看的話你會發現在谷歌的官方文檔中提到了ContentResolver(內容解析者),外界可以通過ContentResolver接口來訪問ContentProvider(內容提供者)中的數據。但是在詳細了解ContentResolver之前有一項工作是必須要做的,那就是先理解Uri,在谷歌文檔中也有介紹,接下來我們就來詳細的學習下Uri這個類
Uri 通用資源標志符(Universal Resource Identifier)Uri代表要操作的數據,Android中可用的每種資源 - 圖像、視頻片段等都可以用Uri來表示。Uri的結構由以下幾個部分組成
scheme、authority、path、query和fragment組成。其中authority又分為host和port。它的格式根據劃分的詳細程度可以分為三種
如下:
~~~
[scheme:][scheme-specific-part][#fragment]
[scheme:][//authority][path][?query][#fragment]?
[scheme:][//host:port][path][?query][#fragment]——最詳細的劃分形式
~~~
看到這里肯定有人糊里糊涂的,接著我們就來舉一個例子來幫助大家詳細的理解Uri這個類的結構
假如有這么一個Uri:[http://www.baidu.com:8080/](http://www.baidu.com:8080/)yourpath/fileName.html?id=15&name=du#dmk
你能將上述Uri進行提取嗎?接著我們就比著標準的格式[scheme:][//host:port][path][?query][#fragment]來將這個Uri各個部分提取出來
scheme:根據標準格式可以看出這里的scheme就是Uri前面//前面的部分這里也就是**http:。**
fragment:dmk這個也是比較容易找到的,在#后面
query:id=15&name=du#dmk。從標準格式可以看到在"#"之前"?"之后的部分是query,在這里當然就是id=15&name=du#dmk了。
authority:從格式二中可以看到authority是在//后的部分,它的終點就是在path之前所以這里的authority就是[www.baidu.com:8080](http://www.baidu.com:8080/)
path:path就是?之前,主機之后的部分那就是yourpath/fileName.html
host和port:因為主機可以分為host和port所以這里的host和port分別為:[www.baidu.com](http://www.baidu.com:8080/)和8080
這里要提醒大家注意的是:在Uri中并不是上述所有的字段都必須有的除了scheme、authority是必須要有的,其它的幾個path、query、fragment,它們每一個可以選擇性的要或不要,但順序不能變,比方說在上述Uri中沒有path那它的格式就為:[http://www.baidu.com:8080/](http://www.baidu.com:8080/)?id=15&name=du#dmk。
在理解了Uri的格式之后,有的人可能會說Uri的各個字段能否用代碼獲取?答案是肯定的
這里我們同樣以[http://www.baidu.com:8080/](http://www.baidu.com:8080/)yourpath/fileName.html?id=15&name=du#dmk為例
- **getScheme() :**獲取Uri中的scheme字符串部分,在這里是http
- **getSchemeSpecificPart():**獲取Uri中的scheme-specific-part:部分,這里是:[http://www.baidu.com:8080/](http://www.baidu.com:8080/)yourpath/fileName.html?
- **getFragment():**獲取Uri中的fragment部分,即dmk
- **getAuthority():**獲取Uri中Authority部分,即[www.baidu.com:8080](http://www.baidu.com:8080/)
- **getPath():**獲取Uri中path部分,即/yourpath/fileName.html
- **getQuery():**獲取Uri中的query部分,即id=15&name=du
- **getHost():**獲取Authority中的Host字符串,即[www.baidu.com](http://www.baidu.com:8080/)
- **getPost():**獲取Authority中的Port字符串,即8080
到這里關于Uri的介紹就完了(這里的關于Uri的介紹的內容主要來自:[Uri詳解之——Uri結構與代碼提取](http://blog.csdn.net/harvic880925/article/details/44679239)?和?[Java魔法堂:URI、URL(含URL Protocol Handler](http://www.cnblogs.com/fsjohnhuang/p/4280369.html))
**5.ContentResolver講解**
在了解了Uri之后就可以來學習學習ContentResolver了,前面我們說到ContentProvider共享數據是通過定義一個對外開放的統一的接口來實現的。然而,應用程序并不直接調用這些方法,而是使用一個 ContentResolver 對象,調用它的方法作為替代。ContentResolver可以與任意內容提供者進行會話,與其合作來對所有相關交互通訊進行管理。當外部應用需要對ContentProvider中的數據進行添加、刪除、修改和查詢操作時,可以使用ContentResolver類來完成,要獲取ContentResolver對象,可以使用Context提供的getContentResolver()方法。ContentResolver?cr?=?getContentResolver();在上面我們提到ContentProvider可以向其他應用程序提供數據,與之對應的ContentResolver則負責獲取ContentProvider提供的數據,修改、添加、刪除更新數據等;
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和對其中的什么數據進行操作,假設給定的是?Uri.parse(“content://com.qstingda.provider.personprovider/contact/15”),那么將會對主機名為com.qstingda.provider.personprovider的ContentProvider進行操作,path為contact/15的數據,看到這如果你之前沒有接觸過ContentProvider肯定一頭霧水,沒有關系,這很正常,等我們把理論知識講完后會有實例,相信你看過實例后就會很明白了。
**6.UriMatch**
UriMatcher 類主要用于匹配Uri.這里的匹配是發生在ContentProvider中的,假如我們向ContentProvider中插入一條數據,不可能為所欲為的想怎么干就怎么干,在ContentProvider肯定要做一個判斷,只有在符合條件下才會去執行你想要執行的操作,這里的判斷就是用UriMatch進行匹配,假如是系統的ContentProvider如聯系人、圖庫、視頻庫等,這些系統都提供了Uri我們可以根據系統提供的Uri來操作相應的數據。其實UriMatch的用法非常簡單,查閱谷歌官方文檔你會發現有這么幾個方法
①publicUriMatcher(int code) 它的作用就是創建一個UriMatch對象
②public voidaddURI([String](#) authority,[String](#) path, int code)
它的作用是在ContentProvider添加一個用于匹配的Uri,當匹配成功時返回code。Uri可以是精確的字符串,Uri中帶有*表示可匹配任意text,#表示只能匹配數字。
③public int match([Uri](#) uri) 這里的Uri就是傳過來的要進行驗證,匹配的Uri假如傳過來的是:content://com.example.test/student/#,則content://com.example.test/student/10可以匹配成功,這里的10可以使任意的數字。
**7.ContentObserver用法**
? ? ? ? ?ContentObserver——內容觀察者,從其名字我們可以看出它的作用就是觀察,觀察什么?觀察指定的Uri引起的數據庫的變化,然后通知主線程,根據需求做我們想要做的處理。這樣說大家可能理解的不是特別透徹,這樣跟大家說它可以實現類似于Adapter的notifyDataSetChanged()這個方法的作用,比方說當觀察到ContentProvider的數據變化時會自動調用谷歌工程師給我們提供的好的方法,可以在此方法中通知主線程數據改變等等。那么問題來了,應該怎樣實現這樣的功能呢?首先要做的就是注冊這個觀察者,這里的注冊是在需要監測ContentProvider的應用中進行注冊并不是在ContentProvider中而在ContentProvider中要做的就是當數據變化時進行通知,這里的通知的方法谷歌已經幫我們寫好,直接調用就行了,查看谷歌文檔你會發現在ContentResolver中有這樣的介紹:
public final void?registerContentObserver?(Uri?uri, boolean notifyForDescendents, ContentObserver?observer)
注冊一個觀察者實例,當指定的Uri發生改變時,這個實例會回調實例對象做相應處理。
參數:uri:需要觀察的Uri
notifyForDescendents:如果為true表示以這個Uri為開頭的所有Uri都會被匹配到,
如果為false表示精確匹配,即只會匹配這個給定的Uri。
舉個例子,假如有這么幾個Uri:
~~~
①content://com.example.studentProvider/student
②content://com.example.studentProvider/student/#
③content://com.example.studentProvider/student/10
④content://com.example.studentProvider/student/teacher
~~~
假如觀察的Uri為content://com.example.studentProvider/student,當notifyForDescendents為true時則以這個Uri開頭的Uri的數據變化時都會被捕捉到,在這里也就是①②③④的Uri的數據的變化都能被捕捉到,當notifyForDescendents為false時則只有①中Uri變化時才能被捕捉到。
看到registerContentObserver?這個方法,根據語言基礎我想大家能夠想到ContentResolver中的另一個方法
public final voidunregisterContentObserver([ContentObserver](#)observer)它的作用就是取消對注冊的那個Uri的觀察,這里傳進去的就是在registerContentObserver中傳遞進去的ContentObserver對象。到這關于注冊和解除注冊的ContentObserver可能大家都比較清楚了,那么問題來了,怎么去寫一個ContentObserver呢?其實它的實現很簡單,直接創建一個類繼承ContentObserver需要注意的是這里必須要實現它的構造方法
`public?ContentObserver([Handler](#)handler)`
這里傳進去的是一個Handler對象,這個Handler對象的作用一般要依賴于ContentObserver的另一個方法即
`public void onChange(boolean selfChange)`
這個方法的作用就是當指定的Uri的數據發生變化時會回調該方法,此時可以借助構造方法中的Handler對象將這個變化的消息發送給主線程,當主線程接收到這個消息之后就可以按照我們的需求來完成相應的操作,比如上面提到的類似于Adapter的notifyDataSetChanged()的作用,下面的案例也是完成了這個功能,準備工作完成之后來看一個案例,相信這個案例會讓你對以上知識了解的更加深入。
**8.案例(自定義ContentProvider)**
在真正的開發中我們很少去自定義一個ContentProvider因為ContentProvider是為了更好的去共享數據,我們在開發中很少會遇到這種情況,而遇到更多的則是訪問系統的ContentProvider,系統的ContentProvider谷歌工程師已經幫我們寫好了,我們直接使用就可以了,這里為了讓大家能夠理解ContentProvider更加徹底,我們自定義一個ContentProvider然后在其它應用中來訪問自定義的ContentProvider的數據這個案例的運行效果如下:

? ? 這里的插入數據,是在一個項目中向另一個項目中的ContentProvider中插入一條數據,其他的操作也是,接下來就來看看怎么實現上述的效果。
? ? 在上面我們提到在自定義ContentProvider時需要繼承ContentProvider并實現3中所述的那幾個方法(系統會自動幫你將要復寫的方法羅列出來),那么我們自定義的PeopleContentProvider的代碼如下
~~~
package com.example.contentproviderpractice;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
public class PeopleContentProvider extends ContentProvider {
//這里的AUTHORITY就是我們在AndroidManifest.xml中配置的authorities,這里的authorities可以隨便寫
private static final String AUTHORITY = "com.example.studentProvider";
//匹配成功后的匹配碼
private static final int MATCH_ALL_CODE = 100;
private static final int MATCH_ONE_CODE = 101;
private static UriMatcher uriMatcher;
private SQLiteDatabase db;
private DBOpenHelper openHelper;
private Cursor cursor = null;
//數據改變后指定通知的Uri
private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/student");
//在靜態代碼塊中添加要匹配的 Uri
static {
//匹配不成功返回NO_MATCH(-1)
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
/**
* uriMatcher.addURI(authority, path, code); 其中
* authority:主機名(用于唯一標示一個ContentProvider,這個需要和清單文件中的authorities屬性相同)
* path:路徑路徑(可以用來表示我們要操作的數據,路徑的構建應根據業務而定)
* code:返回值(用于匹配uri的時候,作為匹配成功的返回值)
*/
uriMatcher.addURI(AUTHORITY, "student", MATCH_ALL_CODE);// 匹配記錄集合
uriMatcher.addURI(AUTHORITY, "student/#", MATCH_ONE_CODE);// 匹配單條記錄
}
@Override
public boolean onCreate() {
openHelper = new DBOpenHelper(getContext());
db = openHelper.getWritableDatabase();
return false;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
switch (uriMatcher.match(uri)) {
/**
* 這里如果匹配是uriMatcher.addURI(AUTHORITY, "student",
* MATCH_SUCCESS_CODE);中的Uri,則我們可以在這里對這個ContentProvider中的數據庫
* 進行刪除等操作。這里如果匹配成功,我們將刪除所有的數據
*/
case MATCH_ALL_CODE:
int count=db.delete("personData", null, null);
if(count>0){
notifyDataChanged();
return count;
}
break;
/**
* 這里如果匹配是uriMatcher.addURI(AUTHORITY,
* "student/#",MATCH_ONE_CODE);中的Uri,則說明我們要操作單條記錄
*/
case MATCH_ONE_CODE:
// 這里可以做刪除單條數據的操作。
break;
default:
throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
}
return 0;
}
@Override
public String getType(Uri uri) {
return null;
}
/**
* 插入 使用UriMatch的實例中的match方法對傳過來的 Uri進行匹配。 這里通過ContentResolver傳過來一個Uri,
* 用這個傳過來的Uri跟在ContentProvider中靜態代碼塊中uriMatcher.addURI加入的Uri進行匹配
* 根據匹配的是否成功會返回相應的值,在上述靜態代碼塊中調用uriMatcher.addURI(AUTHORITY,
* "student",MATCH_CODE)這里的MATCH_CODE
* 就是匹配成功的返回值,也就是說假如返回了MATCH_CODE就表示這個Uri匹配成功了
* ,我們就可以按照我們的需求就行操作了,這里uriMatcher.addURI(AUTHORITY,
* "person/data",MATCH_CODE)加入的Uri為:
* content://com.example.studentProvider/student
* ,如果傳過來的Uri跟這個Uri能夠匹配成功,就會按照我們設定的步驟去執行相應的操作
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
int match=uriMatcher.match(uri);
if(match!=MATCH_ALL_CODE){
throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
}
long rawId = db.insert("personData", null, values);
Uri insertUri = ContentUris.withAppendedId(uri, rawId);
if(rawId>0){
notifyDataChanged();
return insertUri;
}
return null;
}
/**
* 查詢 如果uri為
* content://com.example.studentProvider/student則能匹配成功,然后我們可以按照需求執行匹配成功的操作
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
switch (uriMatcher.match(uri)) {
/**
* 如果匹配成功,就根據條件查詢數據并將查詢出的cursor返回
*/
case MATCH_ALL_CODE:
cursor = db.query("personData", null, null, null, null, null, null);
break;
case MATCH_ONE_CODE:
// 根據條件查詢一條數據。。。。
break;
default:
throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
}
return cursor;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
switch (uriMatcher.match(uri)) {
case MATCH_ONE_CODE:
long age = ContentUris.parseId(uri);
selection = "age = ?";
selectionArgs = new String[] { String.valueOf(age) };
int count = db.update("personData", values, selection,selectionArgs);
if(count>0){
notifyDataChanged();
}
break;
case MATCH_ALL_CODE:
// 如果有需求的話,可以對整個表進行操作
break;
default:
throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
}
return 0;
}
//通知指定URI數據已改變
private void notifyDataChanged() {
getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
}
}
~~~
可以看到在onCreate()方法中創建了一個數據庫,關于數據庫的操作大家可以看此博客[http://blog.csdn.net/dmk877/article/details/44876805](http://blog.csdn.net/dmk877/article/details/44876805)。這里就不多做介紹了。注意這個案例牽扯到兩個項目,一個是包含我們自定義的ContentProvider,另一個項目是訪問這個包含ContentProvider項目中的數據。
寫好PeopleContentProvider之后千萬不要忘了在清單文件中注冊
~~~
<provider
android:name="com.example.contentproviderpractice.PeopleContentProvider"
android:authorities="com.example.student"
android:exported="true" >
</provider>
~~~
這里的authorities就是它是唯一標識內容提供者的,為內容提供者指定一個唯一的標識,這樣別的應用才可以唯一獲取此provider,exported的值為[flase|true]當為true時:當前提供者可以被其它應用使用。任何應用可以使用Provider通過URI 來獲得它,也可以通過相應的權限來使用Provider。當為false時:當前提供者不能被其它應用使用,默認為true。注冊好之后運行到手機上,此時其它的應用就可以通過ContentResolver來訪問這個PeopleContentProvider了,具體怎么操作呢?我們再新建一個項目在MainActivity的代碼如下:
~~~
package com.example.otherapplication;
import java.util.ArrayList;
import com.example.otherapplication.adapter.MyAdapter;
import com.example.otherapplication.bean.Student;
import com.example.otherapplication.observer.PersonOberserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;
public class MainActivity extends Activity implements OnClickListener {
private ContentResolver contentResolver;
private ListView lvShowInfo;
private MyAdapter adapter;
private Button btnInit;
private Button btnInsert;
private Button btnDelete;
private Button btnUpdate;
private Button btnQuery;
private Cursor cursor;
private static final String AUTHORITY = "com.example.studentProvider";
private static final Uri STUDENT_ALL_URI = Uri.parse("content://" + AUTHORITY + "/student");
protected static final String TAG = "MainActivity";
private Handler handler=new Handler(){
public void handleMessage(android.os.Message msg) {
//在此我們可以針對數據改變后做一些操作,比方說Adapter.notifyDataSetChanged()等,根據業務需求來定。。
cursor = contentResolver.query(STUDENT_ALL_URI, null, null, null,null);
adapter.changeCursor(cursor);
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lvShowInfo=(ListView) findViewById(R.id.lv_show_info);
initData();
}
private void initData() {
btnInit=(Button) findViewById(R.id.btn_init);
btnInsert=(Button) findViewById(R.id.btn_insert);
btnDelete=(Button) findViewById(R.id.btn_delete);
btnUpdate=(Button) findViewById(R.id.btn_update);
btnQuery=(Button) findViewById(R.id.btn_query);
btnInit.setOnClickListener(this);
btnInsert.setOnClickListener(this);
btnDelete.setOnClickListener(this);
btnUpdate.setOnClickListener(this);
btnQuery.setOnClickListener(this);
contentResolver = getContentResolver();
//注冊內容觀察者
contentResolver.registerContentObserver(STUDENT_ALL_URI,true,new PersonOberserver(handler));
adapter=new MyAdapter(MainActivity.this,cursor);
lvShowInfo.setAdapter(adapter);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
//初始化
case R.id.btn_init:
ArrayList<Student> students = new ArrayList<Student>();
Student student1 = new Student("蒼老師",25,"一個會教學的好老師");
Student student2 = new Student("柳巖",26,"大方");
Student student3 = new Student("楊冪",27,"漂亮");
Student student4 = new Student("張馨予",28,"不知道怎么評價");
Student student5 = new Student("范冰冰",29,"。。。");
students.add(student1);
students.add(student2);
students.add(student3);
students.add(student4);
students.add(student5);
for (Student Student : students) {
ContentValues values = new ContentValues();
values.put("name", Student.getName());
values.put("age", Student.getAge());
values.put("introduce", Student.getIntroduce());
contentResolver.insert(STUDENT_ALL_URI, values);
}
break;
//增
case R.id.btn_insert:
Student student = new Student("小明", 26, "帥氣男人");
//實例化一個ContentValues對象
ContentValues insertContentValues = new ContentValues();
insertContentValues.put("name",student.getName());
insertContentValues.put("age",student.getAge());
insertContentValues.put("introduce",student.getIntroduce());
//這里的uri和ContentValues對象經過一系列處理之后會傳到ContentProvider中的insert方法中,
//在我們自定義的ContentProvider中進行匹配操作
contentResolver.insert(STUDENT_ALL_URI,insertContentValues);
break;
//刪
case R.id.btn_delete:
//刪除所有條目
contentResolver.delete(STUDENT_ALL_URI, null, null);
//刪除_id為1的記錄
Uri delUri = ContentUris.withAppendedId(STUDENT_ALL_URI,1);
contentResolver.delete(delUri, null, null);
break;
//改
case R.id.btn_update:
ContentValues contentValues = new ContentValues();
contentValues.put("introduce","性感");
//更新數據,將age=26的條目的introduce更新為"性感",原來age=26的introduce為"大方".
//生成的Uri為:content://com.example.studentProvider/student/26
Uri updateUri = ContentUris.withAppendedId(STUDENT_ALL_URI,26);
contentResolver.update(updateUri,contentValues, null, null);
break;
//查
case R.id.btn_query:
//通過ContentResolver獲得一個調用ContentProvider對象
Cursor cursor = contentResolver.query(STUDENT_ALL_URI, null, null, null,null);
//CursorAdapter的用法,參考此博客:http://blog.csdn.net/dmk877/article/details/44983491
adapter=new MyAdapter(MainActivity.this,cursor);
lvShowInfo.setAdapter(adapter);
cursor = contentResolver.query(STUDENT_ALL_URI, null, null, null,null);
adapter.changeCursor(cursor);
break;
}
}
}
~~~
? ? 可以看出若想操作我們想操作的ContentProvider,必須要知道內容提供者的Uri,再正確得到Uri之后,就可以通過ContentResolver對象來操作ContentProvider中的數據了,假如你需要插入數據只需要調用contentResolver.insert(uri, contentValues);把正確的uri和ContentValues鍵值對傳過去就行了。執行這句話系統就會根據我們提供的uri找到對應的ContentProvider,因為我們的uri中包含了authority(主機等各種信息),得到對應的ContentProvider后將調用ContentResolver的與之對應的增刪改查方法,并將參數通過ContentResolver的增刪改查方法傳遞到ContentProvider中。在上面用到了CursorAdapter關于CursorAdapter的用法可以參考此博客[http://blog.csdn.net/dmk877/article/details/44983491](http://blog.csdn.net/dmk877/article/details/44983491)
PersonObserver的代碼如下
~~~
package com.example.otherapplication.observer;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Message;
public class PersonOberserver extends ContentObserver {
private Handler handler;
public PersonOberserver(Handler handler) {
super(handler);
this.handler=handler;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
//向handler發送消息,更新查詢記錄
Message msg = new Message();
handler.sendMessage(msg);
}
}
~~~
可以看到,在構造方法中接收了Handler然后當監聽到指定的Uri的數據變化時就會通過Handler消息機制發送一條消息,然后的操作就由我們自行完成了。
到這里我們來理一理整個操作的運行流程:首先有兩個項目,一個是有ContentProvider的,在這個ContentProvider中初始化了一個數據庫,我們的目的就是在另一個項目中來操作這個項目中ContentProvider中的數據,例如插入一條數據,查詢等。對于怎么在另一個項目中操作ContentProvider中的數據,是通過ContentResolver(內容解析者)對象來操作的,假如我們要進行insert操作,那么需要調用ContentResolver的insert(uri, ContentValues);將Uri和ContentValues對象經過一系列操作傳遞到ContentProvider的中,然后在ContentProvider會對這個Uri進行匹配,如果匹配成功則按照我們的需求去執行相應的操作,如:插入數據、查詢數據等。如果想進一步理解ContentProvider和ContentResolver之間的關系[http://blog.csdn.net/u010961631/article/details/14227421](http://blog.csdn.net/u010961631/article/details/14227421)(對這個過程從源碼進行了解析,不建議初學者閱讀)。下面我們來畫一張圖再來說一下這個過程

從圖中可以看出在OtherApplication中注冊了ContentObserver之后,當Application1中的數據庫發生了變化時,只需要在ContentProvider中調用ContentResolver的notifyChange(Uri,[ContentObserver]() observer),由于在OtherApplication中注冊了ContentObserver(注冊時用的Uri和ContentProvider中發生變化的Uri一樣)因此在ContentObserver中會收到這個變化信息,它就可以將這個消息通過Handler發送給OtherApplication。
好了,本篇關于ContentProvider的介紹主要是ContentProvider的基礎知識,以及自定義一個ContentProvider并操作它的執行過程,如果大家發現博客中有錯誤的地方,歡迎批評指正,如有疑問歡迎留言,謝謝
如果你覺著本篇博客對你有幫助,就贊一個,頂一下唄。。。。
[案例源碼地址](http://download.csdn.net/detail/dmk877/9381338)?(注意案例應先運行含有ContentProvider的項目。)
轉載請注明出處:[http://blog.csdn.net/dmk877/article/details/50387741](http://blog.csdn.net/dmk877/article/details/50387741)
參考鏈接:
[http://blog.csdn.net/harvic880925/article/details/44679239](http://blog.csdn.net/harvic880925/article/details/44679239)(Uri詳解)
[http://www.cnblogs.com/fsjohnhuang/p/4280369.html](http://www.cnblogs.com/fsjohnhuang/p/4280369.html)
[http://blog.csdn.net/whyrjj3/article/details/7852800](http://blog.csdn.net/whyrjj3/article/details/7852800)
[http://www.androiddesignpatterns.com/2012/06/content-resolvers-and-content-providers.html](http://www.androiddesignpatterns.com/2012/06/content-resolvers-and-content-providers.html)
[http://blog.csdn.net/liuhe688/article/details/7050868](http://blog.csdn.net/liuhe688/article/details/7050868)
[http://blog.csdn.net/qinjuning/article/details/7047607](http://blog.csdn.net/qinjuning/article/details/7047607)(ContentObserver用法詳解)
[http://blog.csdn.net/u010961631/article/details/14227421](http://blog.csdn.net/u010961631/article/details/14227421)(對ContentProvider源碼解析)