




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第8章Android核心組件—ContentProvider本章簡介ContentProvider是Android為應用程序開發提供的核心組件之一。它是Android系統中不同應用程序間進行數據交換的標準API。ContentProvider的主要功能有數據存儲、數據檢索以及為其它應用程序提供數據訪問接口。本章目錄8.1ContentProvider簡介8.2ContentProvider的共享數據模型8.3URI8.4ContentResolver8.5創建ContentProvider8.6使用ContentResolver8.7訪問系統內置的ContentProvider8.8實例練習——掌上個人圖書管理系統8.9小結8.10習題8.1ContentProvider簡介ContentProvider主要用于為不同應用程序之間提供數據共享功能。它提供了一套完整的機制,允許不同應用程序進行數據交換。與SharedPreferences、文件存儲等全局數據共享模式不同,ContentProvider可以選擇對一部分數據進行共享。因此,它能夠有效保證應用程序中隱私數據的安全性。此外,Android系統為一些經常使用的數據(音樂、視頻、圖像、聯系人等)內置了一系列ContentProvider,當應用程序獲得授權后,便能夠安全地訪問這些ContentProvider。當應用程序間共享數據時,可選擇兩種方法使用ContentProvider:第一種方法是從ContentProvider類派生出一個自定義的子類,第二種方法則是將共享的數據添加到已某一存在的ContentProvider中。需要注意的是,當使用第二種方法時應保證已有的ContentProvider和共享的數據具有相同的數據類型;并且,應用程序具有獲得該ContentProvider的訪問權限。對于ContentProvider來說,最重要的三個概念是數據模型、URI和ContentResolver。(1)數據模型ContentProvider以數據表的方式將其內部存儲的數據提供給外部訪問者使用。在數據表中,每一行數據就是一條記錄,而每一列數據則是具有特定類型和意義的屬性。每一條數據記錄都包含一個名為“_ID”的屬性字段,用于唯一的標識一條記錄。(2)URI每一個ContentProvider都會向外提供若干能夠唯一標識自身數據集的公開URI。如果ContentProvider管理多個數據集,它將為各個數據集分配一個獨立的URI。幾乎所有的ContentProvider操作都會使用到URI,如果要自定義一個ContentProvider子類,可將URI定義為常量。(3)ContentResolverContentResolver類提供了可以對ContentProvider中的數據進行查詢、插入、修改和刪除等操作的一系列方法。ContentResolver與ContentProvider是一種多對多的關系。當ContentProvider在單實例模式下運行時,如果有多個應用程序使用ContentResolver訪問ContentProvider,那么由ContentResolver調用的數據訪問操作將委托給同一個ContentProvider處理。8.2ContentProvider的共享數據模型以應用程序A使用ContentResolver對象訪問應用程序B中存儲的共享數據為例,其共享數據模型見圖8-1。從ContentProvider、ContentResolver和URI的關系來看,無論是ContentProvider還是ContentResolver,它們所提供的CRUD操作(對數據記錄的增、刪、改、查)對象都是URI。即,URI是ContentProvider和ContentResolver進行數據交互的標識。此外,由ContentResolver執行的CRUD操作并不是直接作用在URI上,而是通過委托給該URI所對應的ContentProvider實現的。(1)當應用程序A調用ContentResolver的insert()方法時,相當于調用了綁定了該URI的ContentProvider(屬于應用程序B)的insert()方法。(2)當應用程序A調用ContentResolver的update()方法時,相當于調用了綁定了該URI的ContentProvider的update()方法。(3)當應用程序A調用ContentResolver的delete()方法時,相當于調用了綁定了該URI的ContentProvider的delete()方法。(4)當應用程序A調用ContentResolver的query()方法時,相當于調用了綁定了該Uri的ContentProvider的query()方法。8.3URI當使用ContentReslover訪問共享數據時,需要ContentProvider提供一個類似URL的全局資源標識符——URI(UniformResourceIndentifier)。由ContentProvider的共享數據模型可知,ContentProvider是以數據表的方式將其內部存儲的數據提供給外部訪問者使用的。一個ContentProvider可以包含多張數據表,使用URI能夠唯一地標識出應用程序期望訪問的數據表。例如,可使用下述URI標識出ContentProvider提供給外部訪問的books數據表。content://com.demo.datashare/books可將該URI的內容劃分成三個不同的部分:(1)content://:它是URI的標準前綴。類似于訪問Web網頁經常用到的http協議,訪問ContentProvider的默認協議是content。(2)com.demo.datashare:它是ContentProvider的authorities。為區分不同應用程序的共享資源,避免資源命名沖突,可將authorities設置成應用程序的包名。(3)books:標識共享的資源。一個ContentProvider可共享多張數據表,即多個不同的共享資源。對于各個共享資源,都需要定義出唯一的標識符路徑。當需要訪問不同資源時,該部分內容是動態改變的。此外,URI還可以有這個形式:content://com.demo.datashare/books/2它表示要訪問的資源為books數據表中ID為2的記錄。URI甚至還可以有這個形式:content://com.demo.datashare/books/2/line2它表示要訪問的資源為books數據表中ID為2的記錄的line2字段。當得到了ContentProvider提供的URI字符串后,需要將其解析成Uri對象才能作為ContentReslover訪問共享數據的輸入參數。可使用Uri.parse()方法將URI字符串解析成Uri對象。例如,可使用下述代碼解析上文給定的URI字符串:Uriuri=Uri.parse(“content://com.demo.datashare/books”)8.4ContentResolver對于每一個應用程序來說,如果要訪問ContentProvider中共享的數據,就需要借助于ContentResolver類。可使用Android應用組件(如Activity、Service或者其他Context對象)的成員方法getContentResolver()獲取ContentResolver對象。應用程序一旦獲得了ContentResolver對象后,就可以使用ContentResolver的下述方法對共享數據進行CRUD操作。(1)insert(Uriuri,ContentValuesvalues)該方法用于向uri對象插入values封裝的數據。(2)delete(Uriuri,Stringwhere,String[]selectionArgs)該方法用于刪除uri對象中滿足where條件的所有記錄。(3)update(Uriuri,ContentValuesvalues,Stringwhere,String[]selectionArgs)該方法使用values封裝的數據更新uri對象中所有滿足where條件的記錄。(4)query(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder)該方法用于查詢uri對象中所有滿足selection條件的記錄,并在查詢結果中將projection參數指定的屬性列取出。當使用ContentResolver發起數據操作請求后,Android系統將判斷請求的目標對象(ContentProvider)是否已經啟動并運行。如果發現目標對象尚未啟動,Android系統將自動啟動。8.5創建ContentProvider可按照下述步驟創建ContentProvider。(1)從ContentProvider類派生出一個自定義子類,并在子類中實現父類中的insert()、delete()、update()和query()等抽象方法。(2)在項目AndroidManifest.xml清單文件中注冊自定義子類,并為該類綁定URI資源。8.5.1定義ContentProvider子類8.5.2配置ContentProvider8.5.1定義ContentProvider子類ContentProvider類有6個抽象方法,在派生的子類中需要將這6個方法全部重寫。(1)onCreate()該方法將在初始化ContentProvider時被調用。只有當ContentResolver嘗試訪問應用程序的共享數據時,ContentProvider才會被初始化。通常應將對ContentProvider數據庫的創建和升級操作放入到該方法中執行。(2)query()該方法用于從ContentProvider中查詢數據。它使用uri參數指定查詢的數據表,projection參數指定查詢的屬性列,selection和selectionArgs參數設置查詢條件,sortOrder參數則用于指定查詢結果中記錄的排序方式。該方法用Cursor對象返回查詢結果。(3)insert()該方法用于向ContentProvider插入一條新的記錄。它使用uri參數指定目標數據表,并將插入的記錄保存在values參數中。該方法會在記錄成功插入后,返回插入記錄的URI。(4)update()該方法用于更新ContentProvider的共享數據。它使用uri參數指定目標數據表,values參數保存更新的記錄,selection和selectionArgs參數設置更新條件。該方法會在記錄更新成功后,返回受影響的記錄個數。(5)delete()該方法用于從ContentProvider中刪除數據。它使用uri參數指定目標數據表,selection和selectionArgs參數設置記錄刪除條件。該方法會在記錄刪除后,返回受影響的記錄個數。(6)getType()該方法根據傳入的URI,返回其MIME類型。如果返回的數據包含多條記錄,MIME類型以“vnd.android.cursor.dir/”作為它的字符串前綴;否則,MIME類型的字符串以“vnd.android.cursor.item/”作為前綴。例如,可以用下述代碼定義出一個從ContentProvider類繼承的子類。publicclassMyProviderextendsContentProvider{//第一次創建該ContentProvider時調用@OverridepublicbooleanonCreate(){System.out.println("===onCreate方法被調用===");returnfalse;}//實現查詢方法,返回查詢得到的Cursor@OverridepublicCursorquery(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder){System.out.println(uri+"===query方法被調用===");System.out.println("selection參數為:"+selection);returnnull;}//實現插入方法,返回新插入的記錄的Uri@OverridepublicUriinsert(Uriuri,ContentValuesvalues){System.out.println(uri+"===insert方法被調用===");System.out.println("values參數為:"+values);returnnull;}//實現刪除方法,返回被更新的記錄個數@Overridepublicintupdate(Uriuri,ContentValuesvalues,Stringselection,String[]selectionArgs){System.out.println(uri+"===update方法被調用===");System.out.println("where參數為:"+selection+",values參數為:"+values);return0;}//實現刪除方法,返回被刪除的記錄個數@Overridepublicintdelete(Uriuri,Stringselection,String[]selectionArgs){System.out.println(uri+"===delete方法被調用===");System.out.println("selection參數為:"+selection);return0;}//返回代表了該ContentProvider所提供數據的MIME類型@OverridepublicStringgetType(Uriuri){returnnull;}}在由ContentProvider子類實現的6個抽象方法中都會使用到一個Uri對象,它是由ContentReslover在調用這些同名方法時傳遞輸入的。為了能夠在使用這些方法時唯一地確定出Uri資源的數據存儲位置,Android提供了UriMatcher工具類。它主要提供了如下常用的兩個方法:(1)addURI(Stringauthority,Stringpath,intcode)該方法用于向UriMatcher注冊Uri資源。其中:前兩個輸入參數組合生成一個Uri資源,第三個參數則用于為該Uri資源指定一個整型的標識代碼。(2)match(Uriuri)該方法用于為傳入的uri對象返回ContentProvider中一個最匹配的Uri資源標識代碼。ContentProvider可利用該代碼,判斷出外部應用期望訪問的共享數據的存儲位置。如果沒有為傳入的uri對象查找不到匹配的資源標識符,該方法將會返回-1。下面修改MyProvider子類,為其添加一個UriMatcher對象,以確定出由外部傳遞而來的Uri資源在ContentProvider中的數據存儲位置。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);}…代碼解釋:第一段用粗體字標記的代碼段創建了一個UriMatcher對象,并給出了Uri匹配資源的注冊方法。首先,在MyProvider類中新增加了4個整型常量。其中:TABLE1_DIR表示訪問table1表中的所有記錄,TABLE1_ITEM表示訪問table1表中的單條記錄,TABLE2_DIR表示訪問table2表中的所有記錄,TABLE2_ITEM表示訪問table2表中的單條記錄。然后,使用靜態代碼塊創建了一個UriMatcher對象,并調用該對象的addURI()方法,為其注冊期望匹配的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;}…}…}代碼解釋:第二段用粗體字標記的代碼段給出了如何使用UriMatcher對象的match()方法為外部應用傳入的Uri資源匹配共享數據。例如,當query()方法被調用時,它會利用UriMatcher對象的match()方法判斷出外部應用期望訪問的共享數據表。此外,由MyProvider子類實現的insert()、update()和delete()方法也可采用類似的方法匹配外部傳入的Uri資源。8.5.2配置ContentProviderAndroid應用要求所有的應用程序組件(Activity、Service、ContentProvider等)只有在清單文件下進行配置后,才能被使用。為配置前文所定義的MyProvider子類,只需為<application>標簽添加下述<provider>子標簽。<applicationandroid:icon="@drawable/ic_launcher">…<!—注冊一個ContentProvider--><providerandroid:name=".MyProvider"android:authorities="vider.MyProvider"android:exported="true"/></application>代碼解釋:粗體字標記的代碼段給出了在清單文件中使用<provider>標簽配置MyProvider子類的方法。其中:android:name屬性指定了ContentProvider的實現類類名——.MyProvider,android:authorities屬性指定了外部應用訪問MyProvider時URI資源的authority,android:exported屬性則用于指示MyProvider是否能夠被其他應用調用。8.6使用ContentResolver根據ContentProvider的數據共享模型,當外部應用訪問ContentProvider提供的共享數據時,它不能直接調用ContentProvider實現的query()、insert()、update()和delete()等方法,而是需要借助于ContentResolver提供的同名方法。ContentResolver能夠將共享數據的操作方法委托給任意ContentProvider執行。可使用Context類提供的getContentResolver()方法獲得ContentResolver對象。下面給出一個Activity的示例代碼,說明ContentResolver的使用方法。publicclassMainActivityextendsActivity{ ContentResolvercontentResolver; Uriuri=Uri.parse("content://vider.MyProvider/"); @Override publicvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);
//獲取ContentResolver對象 contentResolver=getContentResolver(); }代碼解釋:第一段用粗體字標記的代碼段給出了ContentResolver對象的獲得方法。可在Activity組件中使用getContentResolver()方法獲得一個ContentResolver對象。當得到ContentResolver對象之后,接下來就可以調用ContentResolver的query()、insert()、update()和delete()方法了。publicvoidquery() {
//調用ContentResolver的query()方法 contentResolver.query(uri,null,"query_where",null,null); } publicvoidinsert() {
ContentValuesvalues=newContentValues(); values.put("name","abc"); //調用ContentResolver的insert()方法 contentResolver.insert(uri,values); }代碼解釋:第二段用粗體字標記的代碼段給出了如何使用ContentResolver.query()方法查詢ContentProvider的共享數據。它通過輸入的uri對象,將查詢請求委托給ContentProvider,再由ContentProvider執行query()方法將查詢結果返回給外部調用組件。第三段用粗體字標記的代碼段給出了如何使用ContentResolver.insert()方法向ContentProvider插入數據。它通過輸入的uri對象,將數據插入操作委托給ContentProvider,再由ContentProvider執行insert()方法插入外部調用組件傳遞而來的數據。publicvoidupdate() {
ContentValuesvalues=newContentValues(); values.put("name","efg"); //調用ContentResolver的update()方法 contentResolver.update(uri,values,"update_where",null); } publicvoiddelete() {
//調用ContentResolver的delete()方法 intcount=contentResolver.delete(uri,"delete_where",null); }}代碼解釋:第四段用粗體字標記的代碼段給出了如何使用ContentResolver.update()方法執行對ContentProvider共享數據的更新。它通過輸入的uri對象,將數據更新操作委托給ContentProvider,再由ContentProvider執行update()方法更新共享數據。第五段用粗體字標記的代碼段給出了如何使用ContentResolver.delete()方法執行對ContentProvider共享數據的刪除操作。它通過輸入的uri對象,將數據刪除操作委托給ContentProvider,再由ContentProvider執行delete()方法刪除滿足條件的共享數據。8.7訪問系統內置的ContentProviderAndroid系統內置了大量的ContentProvider,例如音頻、視頻、圖像和通訊錄等組件。當外部應用程序獲得了對這些ContentProvider的訪問權限后,只需通過Android官方文檔獲取Uri和數據表結構,即可使用ContentReslover來對他們進行數據訪問操作。下面給出示例程序8-1,說明如何開發外部應用查詢圖8-2所示的聯系人信息。它只有一個Activity——MainActivity,如圖8-3所示。當應用程序啟動后,將自動讀取Android系統的通信錄,并將所有聯系人的信息顯示到MainActivity中。為方便外部訪問聯系人信息,Android系統為管理聯系人提供的ContentProvider,它主要向外暴露如下3個URI:(1)ContactsContract.Contracts.CONTENT_URI存儲通信錄數據表。(2)ContactsContract.CommonDataKinds.Phone.CONTENT_URI存儲聯系人電話號碼。(3)ContactsContract.CommonDataKinds.Email.CONTENT_URI存儲聯系人電子郵件。當獲得了ContentProvider可供使用的URI資源后,接下來就可以在外部應用中編寫代碼以使用ContentResolver來訪問聯系人信息。為了能夠將讀取的聯系人信息顯示到MainActivity中。在項目視圖中,雙擊打開“activity_main.xml”布局文件,為其添加一個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>然后,雙擊打開“MainActivity.java”文件,找到onCreate(…)方法,為應用程序添加讀取系統通信錄聯系人信息的代碼。publicclassMainActivityextendsAppCompatActivity{//查詢結果返回列String[]columns={ContactsContract.Contacts.DISPLAY_NAME,ContactsContract.Contacts.ID};
@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
//申請讀取通信錄的系統權限ActivityCompat.requestPermissions(MainActivity.this,newString[]{Manifest.permission.READ_CONTACTS},1);
Stringqueryresult=getQueryData();TextViewresultview=(TextView)findViewById(R.id.result);resultview.setText("ID\t姓名\t電話號碼\n"+queryresult);}代碼解釋:第一段粗體字標記的代碼段給出了為應用程序申請讀取系統通信錄讀取權限的代碼。可使用requestPermissions()為外部應用申請獲取系統敏感資源的權限。該方法需要接收3個輸入參數,第一個參數是申請權限的應用組件;第二個參數是該組件向Android系統申請的具體權限;第三個參數則是權限申請的請求碼,應保證它在應用程序中的唯一性。publicStringgetQueryData(){Stringresult="";//獲取ContentResolver對象ContentResolverresolver=getContentResolver();//獲取聯系人游標Cursorcursor=resolver.query(ContactsContract.Contacts.CONTENT_URI,columns,null,null,null);//獲取聯系人數據表的_ID字段索引int_idIndex=cursor.getColumnIndex(ContactsContract.Contacts.ID);//獲取聯系人數據表的Name字段索引intnameindex=cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);//遍歷Cursor提取聯系人數據while(cursor.moveToNext()){result=result+cursor.getString(_idIndex)+"\t";result=result+cursor.getString(nameindex)+"\t";//獲取電話號碼游標Cursorphonecursor=resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID+"=?",newString[]{cursor.getString(_idIndex)},null);Stringphonenumber="";//遍歷phonecursor提取聯系人電話號碼while(phonecursor.moveToNext()){intnumberFieldColumnIndex=phonecursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);Stringtemp=phonecursor.getString(numberFieldColumnIndex);phonenumber=phonenumber+""+temp;}//關閉電話號碼游標phonecursor.close();result=result+phonenumber+"\t\n";}//關閉聯系人游標cursor.close();returnresult;}}代碼解釋:第二段粗體字標記的代碼段給出了讀取系統通信錄中聯系人信息的代碼。對ContentProvider的操作包含兩種數據類型,分別是聯系人和電話號碼。這兩種數據類型的URI分別是ContactsContract.Contacts.CONTENT_URI和ContactsContract.CommonDataKinds.Phone.CONTENT_URI。數據查詢操作由MainActivity類中自定義方法getQueryData()執行。它首先使用Context.getContentResolver()獲取一個ContentResolver對象;然后使用ContentResolver對象的query()方法通過輸入Uri對象——ContactsContract.Contacts.CONTENT_URI將通信錄中的聯系人姓名和ID全部查詢出來,并返回指向首條記錄的游標以遍歷查詢結果;最后,使用ContentResolver對象的query()方法通過輸入Uri對象——ContactsContract.CommonDataKinds.Phone.CONTENT_URI將指定聯系人的電話號碼查詢出來。由于通訊錄是Android系統中的敏感信息,因此還需要在AndroidManifest.xml文件中為應用程序添加對系統通信錄讀取權限的聲明。<manifestxmlns:android="/apk/res/android"package="com.example.syscontentproviderdemo">
<uses-permissionandroid:name="android.permission.READ_CONTACTS"/><application...</application></manifest>編譯并運行應用程序,可以查詢到通信錄中所有的聯系人信息,見圖8-3所示。8.8實例練習——掌上個人圖書管理系統本實例要實現的是一個可隨時、隨地管理個人圖書信息的應用程序。它主要實現的功能是對讀者個人圖書信息的查詢、插入、更新和刪除。圖8-4、圖8-5、圖8-6和圖8-7分別是應用程序的圖書查詢、圖書插入、圖書更新和圖書刪除界面。通過本實例,讀者可綜合掌握開發ContentProvider組件時經常使用到的SQLite數據庫、ContentProvider、ContentResolver和UriMatcher等技術。可使用下述步驟實現本實例應用程序的開發。(1)第一步:在AndroidStudio中導入CustomContentProviderDemo項目,具體步驟如下。①打開AndroidStudio,選擇OpenanexistingAndroidStudioProject項;②在彈出的對話框中,選擇項目文件所在的根目錄,點擊“OK”按鈕;③AndroidStudio將會自動導入項目文件,并生成項目結構;
該實例程序的項目目錄結構如圖8-8所示。(2)第二步:設計應用程序的底層數據庫,包括數據庫的存儲文件名、數據庫版本號、數據表以及數據表的邏輯結構。此外,為方便外部應用訪問數據庫,還應設計出標識數據庫中共享數據的URI資源。可使用下述代碼為掌上圖書管理系統定義出描述數據庫信息的輔助類(BookTableCPMetaData和BookTableMetaData)。//掌上圖書管理系統數據庫定義publicclassBookTableCPMetaData{publicstaticfinalStringDATABASE_NAME="books.db";publicstaticfinalintDATABASE_VERSION=1;publicstaticfinalStringAUTHORITY="com.example.bookstorecontentprovider.bookprovider";publicstaticfinalStringBOOKS_TABLE_NAME="books";}//掌上圖書管理系統數據表定義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)第三步:使用下述代碼定義一個從ContentProvider類繼承的子類,并在子類中重寫父類的onCreate()、query()、insert()、update()、delete()和getType()方法。publicclassBookTableCPextendsContentProvider{publicstaticfinalStringTAG="BTContentProvider";publicDatabaseHelperopenHelper=null;
//定義數據庫輔助操作類——DatabaseHelperpublicclassDatabaseHelperextendsSQLiteOpenHelper{publicDatabaseHelper(Contextcontext){super(context,BookTableCPMetaData.DATABASE_NAME,null,BookTableCPMetaData.DATABASE_VERSION);}//創建books數據表@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);}//更新數據庫版本@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;//創建UriMatcher對象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);}//首次創建ContentProvider時調用該方法@OverridepublicbooleanonCreate(){Log.i(TAG,"createtable");openHelper=newDatabaseHelper(this.getContext());Log.i(TAG,openHelper.toString());returntrue;}/返回ContentProvider所提供數據的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)第四步:在項目目錄下,打開“AndroidManifest.xml”清單文件,在<application>標簽下添加<provider>子標簽,將步驟三創建的ContentProvider子類注冊到Android系統。<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)第五步:打開項目目錄下的"activity_main.xml"文件,為啟動界面增加4個按鈕分別用于激發調用ContentProvider的query()、insert()、update()和delete()方法,并增加1個文本顯示控件顯示上述操作后的結果。<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. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年西雙版納職業技術學院單招職業傾向性考試題庫完整
- 2025年許昌職業技術學院單招職業技能測試題庫及參考答案1套
- 中介服務傭金合同樣本
- 2025年襄陽科技職業學院單招職業技能測試題庫及參考答案
- 園藝師考試中自我反思的重要性試題及答案
- 2025年西雙版納職業技術學院單招職業傾向性考試題庫參考答案
- 各高校輔導員招聘考試的活動策劃能力考核與試題及答案
- 鄉下改造工程合同樣本
- 媒體倫理考查:記者證試題及答案
- 2025年西安職業技術學院單招職業技能考試題庫及參考答案1套
- 防流感班會課件
- 2025安徽蚌埠市國有資本運營控股集團有限公司招聘4人筆試參考題庫附帶答案詳解
- 2024年中國資源循環集團有限公司招聘筆試真題
- 2025年春季四年級下冊語文第15課《白鵝》課件(統編版)
- 2024北京市大興初二(下)期中數學試卷及答案
- JGT266-2011 泡沫混凝土標準規范
- 宿舍教室報修維修登記表
- 核電廠運行規程3
- 現澆箱梁支架施工方案(共87頁結構圖多附現場照片)
- 自學考試——軍人心理咨詢與治療
- 實際問題與二元一次方程組說課稿12
評論
0/150
提交評論