咱們學了 Android 數據持久化的技術,包括文件存儲、SharedPreferences 存 儲、以及數據庫存儲。不知道你有沒有發現,使用這些持久化技術所保存的數據都只能在當 前應用程序中訪問。雖然文件和 SharedPreferences 存儲中提供了 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE 這兩種操做模式,用於供給其餘的應用程序訪問當前應用 的數據,但這兩種模式在 Android 4.2 版本中都已被廢棄了。爲何呢?由於 Android 官方已 經再也不推薦使用這種方式來實現跨程序數據共享的功能,而是應該使用更加安全可靠的內容 提供器技術。數據庫
可能你會有些疑惑,爲何要將咱們程序中的數據共享給其餘程序呢?固然,這個要視 狀況而定的,好比說帳號和密碼這樣的隱私數據顯然是不能共享給其餘程序的,不過一些可 以讓其餘程序進行二次開發的基礎性數據,咱們仍是能夠選擇將其共享的。例如系統的電話 簿程序,它的數據庫中保存了不少的聯繫人信息,若是這些數據都不容許第三方的程序進行 訪問的話,恐怕不少應用的功能都要大打折扣了。除了電話簿以外,還有短信、媒體庫等程 序都實現了跨程序數據共享的功能,而使用的技術固然就是內容提供器了,下面咱們就來對 這一技術進行深刻的探討。安全
內容提供器簡介app
內容提供器(Content Provider)主要用於在不一樣的應用程序之間實現數據共享的功能, 它提供了一套完整的機制,容許一個程序訪問另外一個程序中的數據,同時還能保證被訪數據 的安全性。目前,使用內容提供器是 Android 實現跨程序共享數據的標準方式。ide
不一樣於文件存儲和 SharedPreferences 存儲中的兩種全局可讀寫操做模式,內容提供器可 以選擇只對哪一部分數據進行共享,從而保證咱們程序中的隱私數據不會有泄漏的風險。學習
內容提供器的用法通常有兩種,一種是使用現有的內容提供器來讀取和操做相應程序中 的數據,另外一種是建立本身的內容提供器給咱們程序的數據提供外部訪問接口。那麼接下來 咱們就一個一個開始學習吧,首先從使用現有的內容提供器開始。spa
7.2 訪問其餘程序中的數據對象
當一個應用程序經過內容提供器對其數據提供了外部訪問接口,任何其餘的應用程序就 均可以對這部分數據進行訪問。Android 系統中自帶的電話簿、短信、媒體庫等程序都提供 了相似的訪問接口,這就使得第三方應用程序能夠充分地利用這部分數據來實現更好的功 能。下面咱們就來看一看,內容提供器究竟是如何使用的。排序
7.2.1 ContentResolver 的基本用法接口
對於每個應用程序來講,若是想要訪問內容提供器中共享的數據,就必定要藉助 ContentResolve 類,能夠經過 Context 中的 getContentResolver() 方法獲取到該類的實例。 ContentResolver 中提供了一系列的方法用於對數據進行 CRUD 操做,其中 insert()方法用於 添加數據,update()方法用於更新數據,delete()方法用於刪除數據,query()方法用於查詢數 據。有沒有似曾相識的感受?沒錯,SQLiteDatabase 中也是使用的這幾個方法來進行 CRUD 操做的,只不過它們在方法參數上稍微有一些區別。ci
不一樣於 SQLiteDatabase,ContentResolver 中的增刪改查方法都是不接收表名參數的,而 是使用一個 Uri 參數代替,這個參數被稱爲內容 URI。內容 URI 給內容提供器中的數據創建 了惟一標識符,它主要由兩部分組成,權限(authority)和路徑(path)。權限是用於對不一樣 的應用程序作區分的,通常爲了不衝突,都會採用程序包名的方式來進行命名。好比某個 程序的包名是 com.example.app ,那麼該程序對應的權限就能夠命名爲 com.example.app. provider。路徑則是用於對同一應用程序中不一樣的表作區分的,一般都會添加到權限的後面。 好比某個程序的數據庫裏存在兩張表,table1 和 table2,這時就能夠將路徑分別命名爲/table1 和/table2,而後把權限和路徑進行組合,內容 URI 就變成了 com.example.app.provider/table1 和 com.example.app.provider/table2。不過,目前還很難辨認出這兩個字符串就是兩個內容 URI,咱們還須要在字符串的頭部加上協議聲明。所以,內容 URI 最標準的格式寫法以下:
content://com.example.app.provider/table1 content://com.example.app.provider/table2
有沒有發現,內容 URI 能夠很是清楚地表達出咱們想要訪問哪一個程序中哪張表裏的數 據。也正是所以,ContentResolver 中的增刪改查方法才都接收 Uri 對象做爲參數,由於使用 表名的話系統將沒法得知咱們指望訪問的是哪一個應用程序裏的表。
在獲得了內容 URI 字符串以後,咱們還須要將它解析成 Uri 對象才能夠做爲參數傳入。 解析的方法也至關簡單,代碼以下所示:
Uri uri = Uri.parse("content://com.example.app.provider/table1")
只須要調用 Uri.parse()方法,就能夠將內容 URI 字符串解析成 Uri 對象了。
如今咱們就可使用這個 Uri 對象來查詢 table1 表中的數據了,代碼以下所示:
Cursor cursor = getContentResolver().query(
uri,
projection, selection, selectionArgs, sortOrder);
這些參數和 SQLiteDatabase 中 query()方法裏的參數很像,但整體來講要簡單一些,畢 竟這是在訪問其餘程序中的數據,不必構建過於複雜的查詢語句。下表對使用到的這部分 參數進行了詳細的解釋。
query()方法參數 |
對應 SQL 部分 |
描述 |
uri |
from table_name |
指定查詢某個應用程序下的某一張表 |
projection |
select column1, column2 |
指定查詢的列名 |
selection |
where column = value |
指定 where 的約束條件 |
selectionArgs |
- |
爲 where 中的佔位符提供具體的值 |
orderBy |
order by column1, column2 |
指定查詢結果的排序方式 |
查詢完成後返回的仍然是一個 Cursor 對象,這時咱們就能夠將數據從 Cursor 對象中逐
個讀取出來了。讀取的思路仍然是經過移動遊標的位置來遍歷 Cursor 的全部行,而後再取出 每一行中相應列的數據,代碼以下所示:
if (cursor != null) {
while (cursor.moveToNext()) {
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
掌握了最難的查詢操做,剩下的增長、修改、刪除操做就更不在話下了。咱們先來看看 如何向 table1 表中添加一條數據,代碼以下所示:
ContentValues values = new ContentValues(); values.put("column1", "text"); values.put("column2", 1); getContentResolver().insert(uri, values);
能夠看到,仍然是將待添加的數據組裝到 ContentValues 中,而後調用 ContentResolver的 insert()方法,將 Uri 和 ContentValues 做爲參數傳入便可。
現 在 如 果 我 們 想 要 更 新 這 條 新 添 加 的 數 據 , 把 column1 的 值 清 空 , 可 以 借 助
ContentResolver 的 update()方法實現,代碼以下所示:
ContentValues values = new ContentValues();
values.put("column1", "");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new
String[] {"text", "1"});
注意上述代碼使用了 selection 和 selectionArgs 參數來對想要更新的數據進行約束,以防 止全部的行都會受影響。
最後,能夠調用 ContentResolver 的 delete()方法將這條數據刪除掉,代碼以下所示:
getContentResolver().delete(uri, "column2 = ?", new String[] { "1" });
到這裏爲止,咱們就把 ContentResolver 中的增刪改查方法所有學完了。是否是感受很是 簡單?由於這些知識早在上一章中學習 SQLiteDatabase 的時候你就已經掌握了,所需特別注 意的就只有 uri 這個參數而已。