學習筆記|AS入門(八) 組件篇之ContentProvider

終於又回到組件篇,Android中很是重要的四大組件--Activity、ContentProvider、BroadcastReceiver和Service,它們分工明確,共同構成了可重用、靈活、低耦合的安卓系統。經過以前的學習,咱們知道Activity主要負責UI加載和頁面跳轉,接下來幾篇就依次介紹後三種組件,本篇先學習ContentProvider,目錄見下:

  • ContentProvider概要
  • 從系統提供的Provider訪問數據
    • 內容URI的組成
    • ContentResolve類
    • 例子:讀取聯繫人的電話
  • 建立本身的Provider
    • UriMater類
    • 自定義一個Provider的步驟
    • 例子:爲student.db建立MyProvider

1.ContentProvider概要android

ContentProvider也有存儲數據的功能,但與上一篇中學習的那三種數據存儲方法不一樣的是,後者保存下的數據只能被該應用程序使用,而前者可讓不一樣應用程序之間進行數據共享,它還能夠選擇只對哪一部分數據進行共享,從而保證程序中的隱私數據不會有泄漏風險。因此組件ContentProvider主要負責存儲和共享數據。數據庫

ContentProvider有兩種形式:可使用現有的內容提供者來讀取和操做相應程序中的數據,也能夠建立本身的內容提供者給這個程序的數據提供外部訪問接口。下面分別學習一下。bash

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

既然ContentProvider有對外共享數據的功能,換句話說,其餘應用程序能夠經過ContentProvider對應用中的數據進行增刪改查,說到這裏是否感到熟悉?上篇學習SQLite數據存儲的時候就提到過能夠實現增刪改查的各類輔助性方法,實際上ContentProvider是對SQLiteOpenHelper的進一步封裝,所以它們使用的方法太像了,只不過再也不用單純的表名指明被操做的表,畢竟如今是其餘程序訪問它,而是用有必定格式規範的內容URI來代替。下面先來學習URI的組成。ide

(1)內容URI的組成佈局

以上篇最後作的關於數據庫的demo爲例,它的包名是com.example.myapplication,若是其餘程序想訪問該程序student.db中的student表,那麼須要的內容URI如圖所示:post

能夠看出內容 URI 能夠很是清楚地表達出咱們想要訪問哪一個程序中哪張表裏的數據,但還沒完,還須要將它解析成 Uri 對象才能夠做爲參數傳入。經過調用 Uri.parse()方法,就能夠將內容 URI 字符串解析成 Uri 對象了,代碼以下:學習

(2)ContentResolve類測試

如今有了酷似「表名」的Uri,相似的,在ContentResolver類中提供的一系列用於對數據進行增刪改查操做的方法也酷似SQLiteDatabase的那些輔助性方法: insert() 方法用於添加數據, update() 方法用於更新數據, delete() 方法用於刪除數據, query() 方法用於查詢數據。它們不只方法名同樣,連提供的參數都很是類似,見下圖,紅色部分是區別:ui

因此其餘程序若想要訪問ContentProvider中共享的數據的方法是:

第一步:經過Context 中的 getContentResolver() 方法實例化一個ContentResolve對象。

第二步:調用該對象的增刪改查方法去操做ContentProvider中的數據。 接下來經過讀取聯繫人電話的小例子體驗這個過程:

(3)例子:讀取聯繫人的電話

先在虛擬機上手動添加兩個聯繫人和電話:

這樣準備工做就作好了。而後建個佈局,這裏咱們但願讀取出來的聯繫人的信息可以在ListView中顯示:

在活動中的onCreate()方法中,首先獲取ListView控件的實例,並給它設置適配器,用ArrayAdapter就能夠;而後調用init()方法去實現讀取聯繫人電話的需求。

在init()方法中使用了ContentResolver的query()方法來查詢系統的聯繫人數據。不過這裏傳入的 Uri 參數是一個常量ContactsContract.Contacts.CONTENT_URI,這是由於ContactsContract.Contacts類已經幫咱們作好了封裝,而這個常量就是使用 Uri.parse()方法解析出來的結果。接下來的過程就很熟悉了:用Cursor 對象進行遍歷,先取出聯繫人姓名,這一列對應的常量是是ContactsContract.Contacts.DISPLAY_NAME,以後取出聯繫人的電話時,又進行一次遍歷,這是由於一個聯繫人可能有多個電話,因此須要用ID惟一標識某個聯繫人而後到另外一個表找他的全部電話。等名字和電話都取出以後,將它們拼接起來再添加到 ListView裏。最後千萬不要忘記將Cursor對象關閉掉。

由於讀取系統聯繫人也是須要聲明權限的,必定在配置文件中聲明好:

另外,Android6.0以上的系統要求部分權限還要手動申請,所以在活動中務必要添加一段代碼:

這段代碼很通用,不一樣權限只要更換名稱就可用了:

final private int REQUEST_CODE_ASK_PERMISSIONS = 123;

  int hasWriteContactsPermission = ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS);
        if(hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},REQUEST_CODE_ASK_PERMISSIONS);
            }
            return;
        }
複製代碼

須要運行時權限的就是下圖這幾個:

運行程序跑一下看看吧!下圖展現的內容就是上面一段代碼的做用,選擇ALLOW:

如今就能看到以前添加的兩個聯繫人的姓名和電話了!

3.建立本身的Provider

(1)UriMater類

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

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

步驟一:新建一個類去繼承ContentProvider。

步驟二:重寫ContentProvider的六個抽象方法,方法及含義如圖:

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

android:authorities即Provider的權限,形式是包名.provider

android:name即Provider的全名,形式是包名.類名

android:exported="true" 指明該Provider可被其它程序訪問。

以上這些知識仍是有點抽象,那仍是再來個例子更深入感覺一下吧!

(3)例子:爲student.db建立MyProvider

接下里仍是給上篇的數據庫demo建立一個自定義提供器MyProvider,而後在別的應用程序中經過MyProvider去操做student.db中的數據。

先修改MyHelper,將Toast提示語句都去掉,由於跨程序訪問時咱們不能直接使用 Toast。

而後開始自定義提供器吧!一開始定義了四個常量,分別表示訪問student表中的全部數據、訪問student表中的單條數據(student/#用於表示student表中任意一行記錄)、訪問course表中的全部數據和訪問course表中的單條數據。而後在靜態代碼塊裏對UriMatcher進行了初始化操做,將指望匹配的幾種URI格式添加了進去。

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

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

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

再看insert()方法,一樣的,先獲取到SQLiteDatabase的實例,而後根據傳入的Uri參數判斷出用戶想要往哪張表裏添加數據,再調用SQLiteDatabase的insert()方法進行添加就能夠了。注意insert()方法要求返回一個可以表示這條新增數據的 URI,因此還須要調用Uri.parse()方法將一個以新增數據的id結尾的內容URI解析成Uri對象。

再來看delete()方法,和前面同樣的,不一樣的是這裏須要在調用SQLiteDatabase的delete()方法刪除特定記錄的同時還要把被刪除的行數做爲返回值返回。

終於到了最後一個方法update(),和delete()類似的,在調用SQLiteDatabase的 update()方法進行更新的同時還要把受影響的行數做爲返回值返回。

最後將MyProvider在AndroidManifest.xml文件中註冊,一個自定義內容提供器終於完成了!

如今須要作的是將該程序從模擬器中卸載防止以前產生的遺留數據對後面操做有干擾,而後再運行一下從新安裝在模擬器上,啓動後直接關閉掉。接下來建立一個新的module,注意包要不一樣,表明其餘程序。新建一個佈局test.xml並放四個按鈕:

接下來在活動分別處理四個按鈕的點擊事件。到目前爲止這是第三次用增刪改出方法去操做數據了,相信這些代碼已經不難理解了!調用Uri.parse()將一個內容URI解析成Uri對象,這裏但願操做student.db中的student表。又獲取到ContentResolver對象就能夠進行CRUD操做了,這裏插入兩條記錄,而且經過第一條記錄的insert()方法獲得一個Uri對象,這個對象中包含了新增記錄的id,調用getPathSegments()方法將它取出,以後利用這個id合成新的內容URI和Uri對象方便給該條記錄進行更改和刪除的操做。查詢操做完成的打印出表中全部的數據。

如今運行這個程序,分別進行如下幾個測試,觀察打印出的數據的變化,和預想是同樣的!

關於ContentProvider的知識點就到這裏~

>下一篇預告:組件篇之BroadcastReceiver

相關文章
相關標籤/搜索