Android應(yīng)用程序開(kāi)發(fā) 課件 第8章 Android核心組件-ContentProvider_第1頁(yè)
Android應(yīng)用程序開(kāi)發(fā) 課件 第8章 Android核心組件-ContentProvider_第2頁(yè)
Android應(yīng)用程序開(kāi)發(fā) 課件 第8章 Android核心組件-ContentProvider_第3頁(yè)
Android應(yīng)用程序開(kāi)發(fā) 課件 第8章 Android核心組件-ContentProvider_第4頁(yè)
Android應(yīng)用程序開(kāi)發(fā) 課件 第8章 Android核心組件-ContentProvider_第5頁(yè)
已閱讀5頁(yè),還剩44頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第8章Android核心組件—ContentProvider本章簡(jiǎn)介ContentProvider是Android為應(yīng)用程序開(kāi)發(fā)提供的核心組件之一。它是Android系統(tǒng)中不同應(yīng)用程序間進(jìn)行數(shù)據(jù)交換的標(biāo)準(zhǔn)API。ContentProvider的主要功能有數(shù)據(jù)存儲(chǔ)、數(shù)據(jù)檢索以及為其它應(yīng)用程序提供數(shù)據(jù)訪問(wèn)接口。本章目錄8.1ContentProvider簡(jiǎn)介8.2ContentProvider的共享數(shù)據(jù)模型8.3URI8.4ContentResolver8.5創(chuàng)建ContentProvider8.6使用ContentResolver8.7訪問(wèn)系統(tǒng)內(nèi)置的ContentProvider8.8實(shí)例練習(xí)——掌上個(gè)人圖書(shū)管理系統(tǒng)8.9小結(jié)8.10習(xí)題8.1ContentProvider簡(jiǎn)介ContentProvider主要用于為不同應(yīng)用程序之間提供數(shù)據(jù)共享功能。它提供了一套完整的機(jī)制,允許不同應(yīng)用程序進(jìn)行數(shù)據(jù)交換。與SharedPreferences、文件存儲(chǔ)等全局?jǐn)?shù)據(jù)共享模式不同,ContentProvider可以選擇對(duì)一部分?jǐn)?shù)據(jù)進(jìn)行共享。因此,它能夠有效保證應(yīng)用程序中隱私數(shù)據(jù)的安全性。此外,Android系統(tǒng)為一些經(jīng)常使用的數(shù)據(jù)(音樂(lè)、視頻、圖像、聯(lián)系人等)內(nèi)置了一系列ContentProvider,當(dāng)應(yīng)用程序獲得授權(quán)后,便能夠安全地訪問(wèn)這些ContentProvider。當(dāng)應(yīng)用程序間共享數(shù)據(jù)時(shí),可選擇兩種方法使用ContentProvider:第一種方法是從ContentProvider類派生出一個(gè)自定義的子類,第二種方法則是將共享的數(shù)據(jù)添加到已某一存在的ContentProvider中。需要注意的是,當(dāng)使用第二種方法時(shí)應(yīng)保證已有的ContentProvider和共享的數(shù)據(jù)具有相同的數(shù)據(jù)類型;并且,應(yīng)用程序具有獲得該ContentProvider的訪問(wèn)權(quán)限。對(duì)于ContentProvider來(lái)說(shuō),最重要的三個(gè)概念是數(shù)據(jù)模型、URI和ContentResolver。(1)數(shù)據(jù)模型ContentProvider以數(shù)據(jù)表的方式將其內(nèi)部存儲(chǔ)的數(shù)據(jù)提供給外部訪問(wèn)者使用。在數(shù)據(jù)表中,每一行數(shù)據(jù)就是一條記錄,而每一列數(shù)據(jù)則是具有特定類型和意義的屬性。每一條數(shù)據(jù)記錄都包含一個(gè)名為“_ID”的屬性字段,用于唯一的標(biāo)識(shí)一條記錄。(2)URI每一個(gè)ContentProvider都會(huì)向外提供若干能夠唯一標(biāo)識(shí)自身數(shù)據(jù)集的公開(kāi)URI。如果ContentProvider管理多個(gè)數(shù)據(jù)集,它將為各個(gè)數(shù)據(jù)集分配一個(gè)獨(dú)立的URI。幾乎所有的ContentProvider操作都會(huì)使用到URI,如果要自定義一個(gè)ContentProvider子類,可將URI定義為常量。(3)ContentResolverContentResolver類提供了可以對(duì)ContentProvider中的數(shù)據(jù)進(jìn)行查詢、插入、修改和刪除等操作的一系列方法。ContentResolver與ContentProvider是一種多對(duì)多的關(guān)系。當(dāng)ContentProvider在單實(shí)例模式下運(yùn)行時(shí),如果有多個(gè)應(yīng)用程序使用ContentResolver訪問(wèn)ContentProvider,那么由ContentResolver調(diào)用的數(shù)據(jù)訪問(wèn)操作將委托給同一個(gè)ContentProvider處理。8.2ContentProvider的共享數(shù)據(jù)模型以應(yīng)用程序A使用ContentResolver對(duì)象訪問(wèn)應(yīng)用程序B中存儲(chǔ)的共享數(shù)據(jù)為例,其共享數(shù)據(jù)模型見(jiàn)圖8-1。從ContentProvider、ContentResolver和URI的關(guān)系來(lái)看,無(wú)論是ContentProvider還是ContentResolver,它們所提供的CRUD操作(對(duì)數(shù)據(jù)記錄的增、刪、改、查)對(duì)象都是URI。即,URI是ContentProvider和ContentResolver進(jìn)行數(shù)據(jù)交互的標(biāo)識(shí)。此外,由ContentResolver執(zhí)行的CRUD操作并不是直接作用在URI上,而是通過(guò)委托給該URI所對(duì)應(yīng)的ContentProvider實(shí)現(xiàn)的。(1)當(dāng)應(yīng)用程序A調(diào)用ContentResolver的insert()方法時(shí),相當(dāng)于調(diào)用了綁定了該URI的ContentProvider(屬于應(yīng)用程序B)的insert()方法。(2)當(dāng)應(yīng)用程序A調(diào)用ContentResolver的update()方法時(shí),相當(dāng)于調(diào)用了綁定了該URI的ContentProvider的update()方法。(3)當(dāng)應(yīng)用程序A調(diào)用ContentResolver的delete()方法時(shí),相當(dāng)于調(diào)用了綁定了該URI的ContentProvider的delete()方法。(4)當(dāng)應(yīng)用程序A調(diào)用ContentResolver的query()方法時(shí),相當(dāng)于調(diào)用了綁定了該Uri的ContentProvider的query()方法。8.3URI當(dāng)使用ContentReslover訪問(wèn)共享數(shù)據(jù)時(shí),需要ContentProvider提供一個(gè)類似URL的全局資源標(biāo)識(shí)符——URI(UniformResourceIndentifier)。由ContentProvider的共享數(shù)據(jù)模型可知,ContentProvider是以數(shù)據(jù)表的方式將其內(nèi)部存儲(chǔ)的數(shù)據(jù)提供給外部訪問(wèn)者使用的。一個(gè)ContentProvider可以包含多張數(shù)據(jù)表,使用URI能夠唯一地標(biāo)識(shí)出應(yīng)用程序期望訪問(wèn)的數(shù)據(jù)表。例如,可使用下述URI標(biāo)識(shí)出ContentProvider提供給外部訪問(wèn)的books數(shù)據(jù)表。content://com.demo.datashare/books可將該URI的內(nèi)容劃分成三個(gè)不同的部分:(1)content://:它是URI的標(biāo)準(zhǔn)前綴。類似于訪問(wèn)Web網(wǎng)頁(yè)經(jīng)常用到的http協(xié)議,訪問(wèn)ContentProvider的默認(rèn)協(xié)議是content。(2)com.demo.datashare:它是ContentProvider的authorities。為區(qū)分不同應(yīng)用程序的共享資源,避免資源命名沖突,可將authorities設(shè)置成應(yīng)用程序的包名。(3)books:標(biāo)識(shí)共享的資源。一個(gè)ContentProvider可共享多張數(shù)據(jù)表,即多個(gè)不同的共享資源。對(duì)于各個(gè)共享資源,都需要定義出唯一的標(biāo)識(shí)符路徑。當(dāng)需要訪問(wèn)不同資源時(shí),該部分內(nèi)容是動(dòng)態(tài)改變的。此外,URI還可以有這個(gè)形式:content://com.demo.datashare/books/2它表示要訪問(wèn)的資源為books數(shù)據(jù)表中ID為2的記錄。URI甚至還可以有這個(gè)形式:content://com.demo.datashare/books/2/line2它表示要訪問(wèn)的資源為books數(shù)據(jù)表中ID為2的記錄的line2字段。當(dāng)?shù)玫搅薈ontentProvider提供的URI字符串后,需要將其解析成Uri對(duì)象才能作為ContentReslover訪問(wèn)共享數(shù)據(jù)的輸入?yún)?shù)??墒褂肬ri.parse()方法將URI字符串解析成Uri對(duì)象。例如,可使用下述代碼解析上文給定的URI字符串:Uriuri=Uri.parse(“content://com.demo.datashare/books”)8.4ContentResolver對(duì)于每一個(gè)應(yīng)用程序來(lái)說(shuō),如果要訪問(wèn)ContentProvider中共享的數(shù)據(jù),就需要借助于ContentResolver類。可使用Android應(yīng)用組件(如Activity、Service或者其他Context對(duì)象)的成員方法getContentResolver()獲取ContentResolver對(duì)象。應(yīng)用程序一旦獲得了ContentResolver對(duì)象后,就可以使用ContentResolver的下述方法對(duì)共享數(shù)據(jù)進(jìn)行CRUD操作。(1)insert(Uriuri,ContentValuesvalues)該方法用于向uri對(duì)象插入values封裝的數(shù)據(jù)。(2)delete(Uriuri,Stringwhere,String[]selectionArgs)該方法用于刪除uri對(duì)象中滿足where條件的所有記錄。(3)update(Uriuri,ContentValuesvalues,Stringwhere,String[]selectionArgs)該方法使用values封裝的數(shù)據(jù)更新uri對(duì)象中所有滿足where條件的記錄。(4)query(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder)該方法用于查詢uri對(duì)象中所有滿足selection條件的記錄,并在查詢結(jié)果中將projection參數(shù)指定的屬性列取出。當(dāng)使用ContentResolver發(fā)起數(shù)據(jù)操作請(qǐng)求后,Android系統(tǒng)將判斷請(qǐng)求的目標(biāo)對(duì)象(ContentProvider)是否已經(jīng)啟動(dòng)并運(yùn)行。如果發(fā)現(xiàn)目標(biāo)對(duì)象尚未啟動(dòng),Android系統(tǒng)將自動(dòng)啟動(dòng)。8.5創(chuàng)建ContentProvider可按照下述步驟創(chuàng)建ContentProvider。(1)從ContentProvider類派生出一個(gè)自定義子類,并在子類中實(shí)現(xiàn)父類中的insert()、delete()、update()和query()等抽象方法。(2)在項(xiàng)目AndroidManifest.xml清單文件中注冊(cè)自定義子類,并為該類綁定URI資源。8.5.1定義ContentProvider子類8.5.2配置ContentProvider8.5.1定義ContentProvider子類ContentProvider類有6個(gè)抽象方法,在派生的子類中需要將這6個(gè)方法全部重寫(xiě)。(1)onCreate()該方法將在初始化ContentProvider時(shí)被調(diào)用。只有當(dāng)ContentResolver嘗試訪問(wèn)應(yīng)用程序的共享數(shù)據(jù)時(shí),ContentProvider才會(huì)被初始化。通常應(yīng)將對(duì)ContentProvider數(shù)據(jù)庫(kù)的創(chuàng)建和升級(jí)操作放入到該方法中執(zhí)行。(2)query()該方法用于從ContentProvider中查詢數(shù)據(jù)。它使用uri參數(shù)指定查詢的數(shù)據(jù)表,projection參數(shù)指定查詢的屬性列,selection和selectionArgs參數(shù)設(shè)置查詢條件,sortOrder參數(shù)則用于指定查詢結(jié)果中記錄的排序方式。該方法用Cursor對(duì)象返回查詢結(jié)果。(3)insert()該方法用于向ContentProvider插入一條新的記錄。它使用uri參數(shù)指定目標(biāo)數(shù)據(jù)表,并將插入的記錄保存在values參數(shù)中。該方法會(huì)在記錄成功插入后,返回插入記錄的URI。(4)update()該方法用于更新ContentProvider的共享數(shù)據(jù)。它使用uri參數(shù)指定目標(biāo)數(shù)據(jù)表,values參數(shù)保存更新的記錄,selection和selectionArgs參數(shù)設(shè)置更新條件。該方法會(huì)在記錄更新成功后,返回受影響的記錄個(gè)數(shù)。(5)delete()該方法用于從ContentProvider中刪除數(shù)據(jù)。它使用uri參數(shù)指定目標(biāo)數(shù)據(jù)表,selection和selectionArgs參數(shù)設(shè)置記錄刪除條件。該方法會(huì)在記錄刪除后,返回受影響的記錄個(gè)數(shù)。(6)getType()該方法根據(jù)傳入的URI,返回其MIME類型。如果返回的數(shù)據(jù)包含多條記錄,MIME類型以“vnd.android.cursor.dir/”作為它的字符串前綴;否則,MIME類型的字符串以“vnd.android.cursor.item/”作為前綴。例如,可以用下述代碼定義出一個(gè)從ContentProvider類繼承的子類。publicclassMyProviderextendsContentProvider{//第一次創(chuàng)建該ContentProvider時(shí)調(diào)用@OverridepublicbooleanonCreate(){System.out.println("===onCreate方法被調(diào)用===");returnfalse;}//實(shí)現(xiàn)查詢方法,返回查詢得到的Cursor@OverridepublicCursorquery(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder){System.out.println(uri+"===query方法被調(diào)用===");System.out.println("selection參數(shù)為:"+selection);returnnull;}//實(shí)現(xiàn)插入方法,返回新插入的記錄的Uri@OverridepublicUriinsert(Uriuri,ContentValuesvalues){System.out.println(uri+"===insert方法被調(diào)用===");System.out.println("values參數(shù)為:"+values);returnnull;}//實(shí)現(xiàn)刪除方法,返回被更新的記錄個(gè)數(shù)@Overridepublicintupdate(Uriuri,ContentValuesvalues,Stringselection,String[]selectionArgs){System.out.println(uri+"===update方法被調(diào)用===");System.out.println("where參數(shù)為:"+selection+",values參數(shù)為:"+values);return0;}//實(shí)現(xiàn)刪除方法,返回被刪除的記錄個(gè)數(shù)@Overridepublicintdelete(Uriuri,Stringselection,String[]selectionArgs){System.out.println(uri+"===delete方法被調(diào)用===");System.out.println("selection參數(shù)為:"+selection);return0;}//返回代表了該ContentProvider所提供數(shù)據(jù)的MIME類型@OverridepublicStringgetType(Uriuri){returnnull;}}在由ContentProvider子類實(shí)現(xiàn)的6個(gè)抽象方法中都會(huì)使用到一個(gè)Uri對(duì)象,它是由ContentReslover在調(diào)用這些同名方法時(shí)傳遞輸入的。為了能夠在使用這些方法時(shí)唯一地確定出Uri資源的數(shù)據(jù)存儲(chǔ)位置,Android提供了UriMatcher工具類。它主要提供了如下常用的兩個(gè)方法:(1)addURI(Stringauthority,Stringpath,intcode)該方法用于向UriMatcher注冊(cè)Uri資源。其中:前兩個(gè)輸入?yún)?shù)組合生成一個(gè)Uri資源,第三個(gè)參數(shù)則用于為該Uri資源指定一個(gè)整型的標(biāo)識(shí)代碼。(2)match(Uriuri)該方法用于為傳入的uri對(duì)象返回ContentProvider中一個(gè)最匹配的Uri資源標(biāo)識(shí)代碼。ContentProvider可利用該代碼,判斷出外部應(yīng)用期望訪問(wèn)的共享數(shù)據(jù)的存儲(chǔ)位置。如果沒(méi)有為傳入的uri對(duì)象查找不到匹配的資源標(biāo)識(shí)符,該方法將會(huì)返回-1。下面修改MyProvider子類,為其添加一個(gè)UriMatcher對(duì)象,以確定出由外部傳遞而來(lái)的Uri資源在ContentProvider中的數(shù)據(jù)存儲(chǔ)位置。publicclassMyProviderextendsContentProvider{publicstaticfinalintTABLE1_DIR=0;publicstaticfinalintTABLE1_ITEM=1;publicstaticfinalintTABLE2_DIR=2;publicstaticfinalintTABLE2_ITEM=3;privatestaticUriMatcheruriMatcher;static{uriMatcher=newUriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(“vider”,“table1”,TABLE1_DIR);uriMatcher.addURI(“vider”,“table1/#”,TABLE1_ITEM);uriMatcher.addURI(“vider”,“table2”,TABLE2_DIR);uriMatcher.addURI(“vider”,“table2/#”,TABLE2_ITEM);}…代碼解釋:第一段用粗體字標(biāo)記的代碼段創(chuàng)建了一個(gè)UriMatcher對(duì)象,并給出了Uri匹配資源的注冊(cè)方法。首先,在MyProvider類中新增加了4個(gè)整型常量。其中:TABLE1_DIR表示訪問(wèn)table1表中的所有記錄,TABLE1_ITEM表示訪問(wèn)table1表中的單條記錄,TABLE2_DIR表示訪問(wèn)table2表中的所有記錄,TABLE2_ITEM表示訪問(wèn)table2表中的單條記錄。然后,使用靜態(tài)代碼塊創(chuàng)建了一個(gè)UriMatcher對(duì)象,并調(diào)用該對(duì)象的addURI()方法,為其注冊(cè)期望匹配的Uri資源。@OverridepublicCursorquery(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder){switch(uriMatcher.match(uri)){caseTABLE1_DIR://查詢table1表中的所有記錄break;caseTABLE1_ITEM://查詢table1表中的一條記錄break;caseTABLE2_DIR://查詢table2表中的所有記錄break;caseTABLE2_ITEM://查詢table2表中的一條記錄break;}…}…}代碼解釋:第二段用粗體字標(biāo)記的代碼段給出了如何使用UriMatcher對(duì)象的match()方法為外部應(yīng)用傳入的Uri資源匹配共享數(shù)據(jù)。例如,當(dāng)query()方法被調(diào)用時(shí),它會(huì)利用UriMatcher對(duì)象的match()方法判斷出外部應(yīng)用期望訪問(wèn)的共享數(shù)據(jù)表。此外,由MyProvider子類實(shí)現(xiàn)的insert()、update()和delete()方法也可采用類似的方法匹配外部傳入的Uri資源。8.5.2配置ContentProviderAndroid應(yīng)用要求所有的應(yīng)用程序組件(Activity、Service、ContentProvider等)只有在清單文件下進(jìn)行配置后,才能被使用。為配置前文所定義的MyProvider子類,只需為<application>標(biāo)簽添加下述<provider>子標(biāo)簽。<applicationandroid:icon="@drawable/ic_launcher">…<!—注冊(cè)一個(gè)ContentProvider--><providerandroid:name=".MyProvider"android:authorities="vider.MyProvider"android:exported="true"/></application>代碼解釋:粗體字標(biāo)記的代碼段給出了在清單文件中使用<provider>標(biāo)簽配置MyProvider子類的方法。其中:android:name屬性指定了ContentProvider的實(shí)現(xiàn)類類名——.MyProvider,android:authorities屬性指定了外部應(yīng)用訪問(wèn)MyProvider時(shí)URI資源的authority,android:exported屬性則用于指示MyProvider是否能夠被其他應(yīng)用調(diào)用。8.6使用ContentResolver根據(jù)ContentProvider的數(shù)據(jù)共享模型,當(dāng)外部應(yīng)用訪問(wèn)ContentProvider提供的共享數(shù)據(jù)時(shí),它不能直接調(diào)用ContentProvider實(shí)現(xiàn)的query()、insert()、update()和delete()等方法,而是需要借助于ContentResolver提供的同名方法。ContentResolver能夠?qū)⒐蚕頂?shù)據(jù)的操作方法委托給任意ContentProvider執(zhí)行??墒褂肅ontext類提供的getContentResolver()方法獲得ContentResolver對(duì)象。下面給出一個(gè)Activity的示例代碼,說(shuō)明ContentResolver的使用方法。publicclassMainActivityextendsActivity{ ContentResolvercontentResolver; Uriuri=Uri.parse("content://vider.MyProvider/"); @Override publicvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);

//獲取ContentResolver對(duì)象 contentResolver=getContentResolver(); }代碼解釋:第一段用粗體字標(biāo)記的代碼段給出了ContentResolver對(duì)象的獲得方法??稍贏ctivity組件中使用getContentResolver()方法獲得一個(gè)ContentResolver對(duì)象。當(dāng)?shù)玫紺ontentResolver對(duì)象之后,接下來(lái)就可以調(diào)用ContentResolver的query()、insert()、update()和delete()方法了。publicvoidquery() {

//調(diào)用ContentResolver的query()方法 contentResolver.query(uri,null,"query_where",null,null); } publicvoidinsert() {

ContentValuesvalues=newContentValues(); values.put("name","abc"); //調(diào)用ContentResolver的insert()方法 contentResolver.insert(uri,values); }代碼解釋:第二段用粗體字標(biāo)記的代碼段給出了如何使用ContentResolver.query()方法查詢ContentProvider的共享數(shù)據(jù)。它通過(guò)輸入的uri對(duì)象,將查詢請(qǐng)求委托給ContentProvider,再由ContentProvider執(zhí)行query()方法將查詢結(jié)果返回給外部調(diào)用組件。第三段用粗體字標(biāo)記的代碼段給出了如何使用ContentResolver.insert()方法向ContentProvider插入數(shù)據(jù)。它通過(guò)輸入的uri對(duì)象,將數(shù)據(jù)插入操作委托給ContentProvider,再由ContentProvider執(zhí)行insert()方法插入外部調(diào)用組件傳遞而來(lái)的數(shù)據(jù)。publicvoidupdate() {

ContentValuesvalues=newContentValues(); values.put("name","efg"); //調(diào)用ContentResolver的update()方法 contentResolver.update(uri,values,"update_where",null); } publicvoiddelete() {

//調(diào)用ContentResolver的delete()方法 intcount=contentResolver.delete(uri,"delete_where",null); }}代碼解釋:第四段用粗體字標(biāo)記的代碼段給出了如何使用ContentResolver.update()方法執(zhí)行對(duì)ContentProvider共享數(shù)據(jù)的更新。它通過(guò)輸入的uri對(duì)象,將數(shù)據(jù)更新操作委托給ContentProvider,再由ContentProvider執(zhí)行update()方法更新共享數(shù)據(jù)。第五段用粗體字標(biāo)記的代碼段給出了如何使用ContentResolver.delete()方法執(zhí)行對(duì)ContentProvider共享數(shù)據(jù)的刪除操作。它通過(guò)輸入的uri對(duì)象,將數(shù)據(jù)刪除操作委托給ContentProvider,再由ContentProvider執(zhí)行delete()方法刪除滿足條件的共享數(shù)據(jù)。8.7訪問(wèn)系統(tǒng)內(nèi)置的ContentProviderAndroid系統(tǒng)內(nèi)置了大量的ContentProvider,例如音頻、視頻、圖像和通訊錄等組件。當(dāng)外部應(yīng)用程序獲得了對(duì)這些ContentProvider的訪問(wèn)權(quán)限后,只需通過(guò)Android官方文檔獲取Uri和數(shù)據(jù)表結(jié)構(gòu),即可使用ContentReslover來(lái)對(duì)他們進(jìn)行數(shù)據(jù)訪問(wèn)操作。下面給出示例程序8-1,說(shuō)明如何開(kāi)發(fā)外部應(yīng)用查詢圖8-2所示的聯(lián)系人信息。它只有一個(gè)Activity——MainActivity,如圖8-3所示。當(dāng)應(yīng)用程序啟動(dòng)后,將自動(dòng)讀取Android系統(tǒng)的通信錄,并將所有聯(lián)系人的信息顯示到MainActivity中。為方便外部訪問(wèn)聯(lián)系人信息,Android系統(tǒng)為管理聯(lián)系人提供的ContentProvider,它主要向外暴露如下3個(gè)URI:(1)ContactsContract.Contracts.CONTENT_URI存儲(chǔ)通信錄數(shù)據(jù)表。(2)ContactsContract.CommonDataKinds.Phone.CONTENT_URI存儲(chǔ)聯(lián)系人電話號(hào)碼。(3)ContactsContract.CommonDataKinds.Email.CONTENT_URI存儲(chǔ)聯(lián)系人電子郵件。當(dāng)獲得了ContentProvider可供使用的URI資源后,接下來(lái)就可以在外部應(yīng)用中編寫(xiě)代碼以使用ContentResolver來(lái)訪問(wèn)聯(lián)系人信息。為了能夠?qū)⒆x取的聯(lián)系人信息顯示到MainActivity中。在項(xiàng)目視圖中,雙擊打開(kāi)“activity_main.xml”布局文件,為其添加一個(gè)TextView控件。<LinearLayoutxmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center_horizontal"tools:context="com.example.syscontentproviderdemo.MainActivity">

<TextViewandroid:id="@+id/result"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text=""/></LinearLayout>然后,雙擊打開(kāi)“MainActivity.java”文件,找到onCreate(…)方法,為應(yīng)用程序添加讀取系統(tǒng)通信錄聯(lián)系人信息的代碼。publicclassMainActivityextendsAppCompatActivity{//查詢結(jié)果返回列String[]columns={ContactsContract.Contacts.DISPLAY_NAME,ContactsContract.Contacts.ID};

@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);

//申請(qǐng)讀取通信錄的系統(tǒng)權(quán)限ActivityCompat.requestPermissions(MainActivity.this,newString[]{Manifest.permission.READ_CONTACTS},1);

Stringqueryresult=getQueryData();TextViewresultview=(TextView)findViewById(R.id.result);resultview.setText("ID\t姓名\t電話號(hào)碼\n"+queryresult);}代碼解釋:第一段粗體字標(biāo)記的代碼段給出了為應(yīng)用程序申請(qǐng)讀取系統(tǒng)通信錄讀取權(quán)限的代碼??墒褂胷equestPermissions()為外部應(yīng)用申請(qǐng)獲取系統(tǒng)敏感資源的權(quán)限。該方法需要接收3個(gè)輸入?yún)?shù),第一個(gè)參數(shù)是申請(qǐng)權(quán)限的應(yīng)用組件;第二個(gè)參數(shù)是該組件向Android系統(tǒng)申請(qǐng)的具體權(quán)限;第三個(gè)參數(shù)則是權(quán)限申請(qǐng)的請(qǐng)求碼,應(yīng)保證它在應(yīng)用程序中的唯一性。publicStringgetQueryData(){Stringresult="";//獲取ContentResolver對(duì)象ContentResolverresolver=getContentResolver();//獲取聯(lián)系人游標(biāo)Cursorcursor=resolver.query(ContactsContract.Contacts.CONTENT_URI,columns,null,null,null);//獲取聯(lián)系人數(shù)據(jù)表的_ID字段索引int_idIndex=cursor.getColumnIndex(ContactsContract.Contacts.ID);//獲取聯(lián)系人數(shù)據(jù)表的Name字段索引intnameindex=cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);//遍歷Cursor提取聯(lián)系人數(shù)據(jù)while(cursor.moveToNext()){result=result+cursor.getString(_idIndex)+"\t";result=result+cursor.getString(nameindex)+"\t";//獲取電話號(hào)碼游標(biāo)Cursorphonecursor=resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID+"=?",newString[]{cursor.getString(_idIndex)},null);Stringphonenumber="";//遍歷phonecursor提取聯(lián)系人電話號(hào)碼while(phonecursor.moveToNext()){intnumberFieldColumnIndex=phonecursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);Stringtemp=phonecursor.getString(numberFieldColumnIndex);phonenumber=phonenumber+""+temp;}//關(guān)閉電話號(hào)碼游標(biāo)phonecursor.close();result=result+phonenumber+"\t\n";}//關(guān)閉聯(lián)系人游標(biāo)cursor.close();returnresult;}}代碼解釋:第二段粗體字標(biāo)記的代碼段給出了讀取系統(tǒng)通信錄中聯(lián)系人信息的代碼。對(duì)ContentProvider的操作包含兩種數(shù)據(jù)類型,分別是聯(lián)系人和電話號(hào)碼。這兩種數(shù)據(jù)類型的URI分別是ContactsContract.Contacts.CONTENT_URI和ContactsContract.CommonDataKinds.Phone.CONTENT_URI。數(shù)據(jù)查詢操作由MainActivity類中自定義方法getQueryData()執(zhí)行。它首先使用Context.getContentResolver()獲取一個(gè)ContentResolver對(duì)象;然后使用ContentResolver對(duì)象的query()方法通過(guò)輸入U(xiǎn)ri對(duì)象——ContactsContract.Contacts.CONTENT_URI將通信錄中的聯(lián)系人姓名和ID全部查詢出來(lái),并返回指向首條記錄的游標(biāo)以遍歷查詢結(jié)果;最后,使用ContentResolver對(duì)象的query()方法通過(guò)輸入U(xiǎn)ri對(duì)象——ContactsContract.CommonDataKinds.Phone.CONTENT_URI將指定聯(lián)系人的電話號(hào)碼查詢出來(lái)。由于通訊錄是Android系統(tǒng)中的敏感信息,因此還需要在AndroidManifest.xml文件中為應(yīng)用程序添加對(duì)系統(tǒng)通信錄讀取權(quán)限的聲明。<manifestxmlns:android="/apk/res/android"package="com.example.syscontentproviderdemo">

<uses-permissionandroid:name="android.permission.READ_CONTACTS"/><application...</application></manifest>編譯并運(yùn)行應(yīng)用程序,可以查詢到通信錄中所有的聯(lián)系人信息,見(jiàn)圖8-3所示。8.8實(shí)例練習(xí)——掌上個(gè)人圖書(shū)管理系統(tǒng)本實(shí)例要實(shí)現(xiàn)的是一個(gè)可隨時(shí)、隨地管理個(gè)人圖書(shū)信息的應(yīng)用程序。它主要實(shí)現(xiàn)的功能是對(duì)讀者個(gè)人圖書(shū)信息的查詢、插入、更新和刪除。圖8-4、圖8-5、圖8-6和圖8-7分別是應(yīng)用程序的圖書(shū)查詢、圖書(shū)插入、圖書(shū)更新和圖書(shū)刪除界面。通過(guò)本實(shí)例,讀者可綜合掌握開(kāi)發(fā)ContentProvider組件時(shí)經(jīng)常使用到的SQLite數(shù)據(jù)庫(kù)、ContentProvider、ContentResolver和UriMatcher等技術(shù)??墒褂孟率霾襟E實(shí)現(xiàn)本實(shí)例應(yīng)用程序的開(kāi)發(fā)。(1)第一步:在AndroidStudio中導(dǎo)入CustomContentProviderDemo項(xiàng)目,具體步驟如下。①打開(kāi)AndroidStudio,選擇OpenanexistingAndroidStudioProject項(xiàng);②在彈出的對(duì)話框中,選擇項(xiàng)目文件所在的根目錄,點(diǎn)擊“OK”按鈕;③AndroidStudio將會(huì)自動(dòng)導(dǎo)入項(xiàng)目文件,并生成項(xiàng)目結(jié)構(gòu);

該實(shí)例程序的項(xiàng)目目錄結(jié)構(gòu)如圖8-8所示。(2)第二步:設(shè)計(jì)應(yīng)用程序的底層數(shù)據(jù)庫(kù),包括數(shù)據(jù)庫(kù)的存儲(chǔ)文件名、數(shù)據(jù)庫(kù)版本號(hào)、數(shù)據(jù)表以及數(shù)據(jù)表的邏輯結(jié)構(gòu)。此外,為方便外部應(yīng)用訪問(wèn)數(shù)據(jù)庫(kù),還應(yīng)設(shè)計(jì)出標(biāo)識(shí)數(shù)據(jù)庫(kù)中共享數(shù)據(jù)的URI資源。可使用下述代碼為掌上圖書(shū)管理系統(tǒng)定義出描述數(shù)據(jù)庫(kù)信息的輔助類(BookTableCPMetaData和BookTableMetaData)。//掌上圖書(shū)管理系統(tǒng)數(shù)據(jù)庫(kù)定義publicclassBookTableCPMetaData{publicstaticfinalStringDATABASE_NAME="books.db";publicstaticfinalintDATABASE_VERSION=1;publicstaticfinalStringAUTHORITY="com.example.bookstorecontentprovider.bookprovider";publicstaticfinalStringBOOKS_TABLE_NAME="books";}//掌上圖書(shū)管理系統(tǒng)數(shù)據(jù)表定義publicstaticfinalclassBookTableMetaDataimplementsBaseColumns{publicstaticfinalStringTABLE_NAME="books";publicstaticfinalStringBOOK_NAME="name";publicstaticfinalStringBOOK_ISBN="isbn";publicstaticfinalStringBOOK_AUTHOR="author";publicstaticfinalStringCREATED_DATE="created";publicstaticfinalStringMODIFIED_DATE="modified";publicstaticfinalUriCONTENT_URI=Uri.parse("content://"+BookTableCPMetaData.AUTHORITY+"/books");publicstaticfinalUriCONTENT_SINGLE_URI=Uri.parse("content://"+BookTableCPMetaData.AUTHORITY+"/books/#");publicstaticfinalStringCONTENT_TYPE="vnd.android.cursor.dir/vnd.androidbook.book";publicstaticfinalStringCONTENT_ITEM_TYPE="vnd.android.cursor.item/vnd.androidbook.book";publicstaticfinalStringDEFAULT_SORT_ORDER="modifiedDESC";}(3)第三步:使用下述代碼定義一個(gè)從ContentProvider類繼承的子類,并在子類中重寫(xiě)父類的onCreate()、query()、insert()、update()、delete()和getType()方法。publicclassBookTableCPextendsContentProvider{publicstaticfinalStringTAG="BTContentProvider";publicDatabaseHelperopenHelper=null;

//定義數(shù)據(jù)庫(kù)輔助操作類——DatabaseHelperpublicclassDatabaseHelperextendsSQLiteOpenHelper{publicDatabaseHelper(Contextcontext){super(context,BookTableCPMetaData.DATABASE_NAME,null,BookTableCPMetaData.DATABASE_VERSION);}//創(chuàng)建books數(shù)據(jù)表@OverridepublicvoidonCreate(SQLiteDatabasedb){Stringsql="CREATETABLE"+BookTableMetaData.TABLE_NAME+"("+BookTableMetaData._ID+"INTEGERPRIMARYKEY,"+BookTableMetaData.BOOK_NAME+"TEXT,"+BookTableMetaData.BOOK_ISBN+"TEXT,"+BookTableMetaData.BOOK_AUTHOR+"TEXT,"+BookTableMetaData.CREATED_DATE+"INTEGER,"+BookTableMetaData.MODIFIED_DATE+"INTEGER"+");";Log.i(TAG,db.getPath());db.execSQL(sql);}//更新數(shù)據(jù)庫(kù)版本@OverridepublicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){Log.i(TAG,"Upgradedatabasefrom"+oldVersion+"to"+newVersion+",whichwilldestroyolddata!");Stringsql="DROPTABLEIFEXISTS"+BookTableMetaData.TABLE_NAME;db.execSQL(sql);onCreate(db);}}privatestaticfinalintINCOMMING_BOOK_COLLECTION_URI_INDICATOR=1;privatestaticfinalintINCOMMING_SINGLE_BOOK_URI_INDICATOR=2;//創(chuàng)建UriMatcher對(duì)象privatestaticUriMatchersUriMatcher=null;static{sUriMatcher=newUriMatcher(UriMatcher.NO_MATCH);sUriMatcher.addURI(BookTableCPMetaData.AUTHORITY,"books",INCOMMING_BOOK_COLLECTION_URI_INDICATOR);sUriMatcher.addURI(BookTableCPMetaData.AUTHORITY,"books/#",INCOMMING_SINGLE_BOOK_URI_INDICATOR);}//首次創(chuàng)建ContentProvider時(shí)調(diào)用該方法@OverridepublicbooleanonCreate(){Log.i(TAG,"createtable");openHelper=newDatabaseHelper(this.getContext());Log.i(TAG,openHelper.toString());returntrue;}/返回ContentProvider所提供數(shù)據(jù)的MIME類型@OverridepublicStringgetType(Uriuri){switch(sUriMatcher.match(uri)){caseINCOMMING_BOOK_COLLECTION_URI_INDICATOR:returnBookTableMetaData.CONTENT_TYPE;caseINCOMMING_SINGLE_BOOK_URI_INDICATOR:returnBookTableMetaData.CONTENT_ITEM_TYPE;default:thrownewIllegalArgumentException("UnknownURI"+uri);}}//插入記錄操作@OverridepublicUriinsert(Uriuri,ContentValuesvalues){Log.i(TAG,"insert()");longnow=Long.valueOf(System.currentTimeMillis());if(values.containsKey(BookTableMetaData.CREATED_DATE)==false){values.put(BookTableMetaData.CREATED_DATE,now);}if(values.containsKey(BookTableMetaData.MODIFIED_DATE)==false){values.put(BookTableMetaData.MODIFIED_DATE,now);}if(values.containsKey(BookTableMetaData.BOOK_NAME)==false){thrownewSQLException("Failedtoinsertrow,becauseBookNameisneeded"+uri);}if(values.containsKey(BookTableMetaData.BOOK_ISBN)==false){values.put(BookTableMetaData.BOOK_ISBN,"UnknownISBN");}if(values.containsKey(BookTableMetaData.BOOK_AUTHOR)==false){values.put(BookTableMetaData.BOOK_AUTHOR,"Unknownauthor");}SQLiteDatabasedb=openHelper.getWritableDatabase();longrowID=db.insert(BookTableMetaData.TABLE_NAME,BookTableMetaData.BOOK_NAME,values);if(rowID>0){UriinsertBookedUri=ContentUris.withAppendedId(BookTableMetaData.CONTENT_URI,rowID);getContext().getContentResolver().notifyChange(insertBookedUri,null);returninsertBookedUri;}thrownewSQLException("Failedtoinsertrowinto"+uri);}//刪除記錄操作@Overridepublicintdelete(Uriuri,Stringselection,String[]selectionArgs){Log.i(TAG,"delete()");SQLiteDatabasedb=openHelper.getWritableDatabase();intcount=0;switch(sUriMatcher.match(uri)){caseINCOMMING_BOOK_COLLECTION_URI_INDICATOR:count=db.delete(BookTableMetaData.TABLE_NAME,selection,selectionArgs);break;caseINCOMMING_SINGLE_BOOK_URI_INDICATOR:StringrowID=uri.getPathSegments().get(1);Stringwhere=BookTableMetaData._ID+"="+rowID+(!TextUtils.isEmpty(selection)?"AND("+selection+')':"");count=db.delete(BookTableMetaData.TABLE_NAME,selection,selectionArgs);break;default:thrownewIllegalArgumentException("UnknownURI"+uri);}this.getContext().getContentResolver().notifyChange(uri,null);returncount;}//更新記錄操作@Overridepublicintupdate(Uriuri,ContentValuesvalues,Stringselection,String[]selectionArgs){Log.i(TAG,"update()");SQLiteDatabasedb=openHelper.getWritableDatabase();intcount=0;switch(sUriMatcher.match(uri)){caseINCOMMING_BOOK_COLLECTION_URI_INDICATOR:count=db.update(BookTableMetaData.TABLE_NAME,values,selection,selectionArgs);break;caseINCOMMING_SINGLE_BOOK_URI_INDICATOR:StringrowID=uri.getPathSegments().get(1);Stringwhere="BookTableMetaData._ID"+"="+rowID+(!TextUtils.isEmpty(selection)?"AND("+selection+')':"");count=db.update(BookTableMetaData.TABLE_NAME,values,where,selectionArgs);break;default:thrownewIllegalArgumentException("UnknownURI"+uri);}getContext().getContentResolver().notifyChange(uri,null);returncount;}//查詢操作@OverridepublicCursorquery(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder){Log.i(TAG,"query()");Cursorcursor=null;SQLiteQueryBuilderqb=null;qb=newSQLiteQueryBuilder();switch(sUriMatcher.match(uri)){caseINCOMMING_BOOK_COLLECTION_URI_INDICATOR:qb.setTables(BookTableMetaData.TABLE_NAME);qb.setProjectionMap(sBookProjectionMap);break;caseINCOMMING_SINGLE_BOOK_URI_INDICATOR:qb.setTables(BookTableMetaData.TABLE_NAME);qb.setProjectionMap(sBookProjectionMap);qb.appendWhere(BookTableMetaData._ID+"="+uri.getPathSegments().get(1));break;default:thrownewIllegalArgumentException("UnknownURI"+uri);}StringorderBy="";if(TextUtils.isEmpty(sortOrder)){orderBy=BookTableMetaData.DEFAULT_SORT_ORDER;}else{orderBy=sortOrder;}SQLiteDatabasedb=openHelper.getReadableDatabase();Cursorc=qb.query(db,projection,selection,selectionArgs,null,null,orderBy);inti=c.getCount();ContentResolvercr=this.getContext().getContentResolver();c.setNotificationUri(cr,uri);returnc;}}(4)第四步:在項(xiàng)目目錄下,打開(kāi)“AndroidManifest.xml”清單文件,在<application>標(biāo)簽下添加<provider>子標(biāo)簽,將步驟三創(chuàng)建的ContentProvider子類注冊(cè)到Android系統(tǒng)。<applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme">…

<providerandroid:authorities="com.example.bookstorecontentprovider.bookprovider"android:name="com.example.bookstorecontentprovider.BookTableCP"android:exported="true"></provider></application>(5)第五步:打開(kāi)項(xiàng)目目錄下的"activity_main.xml"文件,為啟動(dòng)界面增加4個(gè)按鈕分別用于激發(fā)調(diào)用ContentProvider的query()、insert()、update()和delete()方法,并增加1個(gè)文本顯示控件顯示上述操作后的結(jié)果。<LinearLayoutxmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center_horizontal"tools:context=".customcontentproviderdemo.MainActivity">…<Buttonandroid:id="@+id/queryContent"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="查詢ContentProvider"/><But

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論