(歡迎大家加入android技術交流QQ群:209796692)
實現ContentProvider MIME 類型
ContentProvider 有兩個方法返回MIME類型。
getType()
一個對任何provider都要實現的方法。
getStreamTypes()
如果你的provider提供的是檔案,此方法是期望被實現的。
表的MIME類型們
getType()方法返回一個MIME格式的String ,此String描述了由content URI參數計算出的資料類型。Uri 可以是一個模式而不是一個具體的URI;此時,你應該返回對應於那些符合content URI模式的的資料的類型。
對於普通的資料類型,比如文本, HTML或 JPEG,getType()應該返回標準的MIME類型。關於標準MIME類型的一個完整的列表能在IANA MIME Media Types 網站上找到。
對於指向表資料的一行或多行的content URI們,getType()應返回一個Android vendor-specific MIME 格式:
· 類型部分:vnd
· 子類型部分:
· 如果URI指向單行:android.cursor.item/
· 如果URI 指向多行:android.cursor.dir/
· Provider-specific 部分:vnd.<name>.<type>
你要提供 <name> 和 <type>。<name>的值應是一個全域唯一的,並且<type> 的值對於對應的URI模式也需是唯一的。對<name> 的一個好的選擇是用你公司的名字或你的應用的Android包名的一部分。對於<type>的一個好的選擇是使用標誌與URI相關連的表的字串。
例如,如果一個provider的authority是com.example.app.provider,並且它曝露了一個叫做table1的表,那麼表示表中多行的MIME類型就是:
vnd.android.cursor.dir/vnd.com.example.provider.table1
表示單行的MIME類型是:
vnd.android.cursor.item/vnd.com.example.provider.table1
表示檔案的MIME 們
如果你的provider提供檔案,就要實現getStreamTypes()。這個方法對傳入的content URI返回一個由指向檔案們的MIME類型組成的String 數組,你應該跟據MIME類型過慮參數過慮MIME 類型們,所以你只返回那些用戶端真正相要的MIME類型們。
例如,假設一個provider提供影像檔,有 .jpg, .png,和 .gif類型。如果一個應用使用過濾字串image/* (表示某些是"image"的東西)調用ContentResolver.getStreamTypes() ,那麼ContentProvider.getStreamTypes() 方法應返回數組:
{ "image/jpeg", "image/png", "image/gif"}
如果應用只對.jpg 檔案感興趣,它應使用過濾字串 *\/jpeg 調用 ContentResolver.getStreamTypes(),並且 ContentProvider.getStreamTypes()應該返回:
{"image/jpeg"}
如果你的provider沒有提供過濾字串中所請求的MIME, getStreamTypes() 應返回 null.
實現一個Contract(契約)類
Contract class是一個public final 類,它包含有定義URI們的常量,列的名字,MIME類型們,以及其它用於provider的中繼資料。這個類建立了一個provider和其它應用之間的契約以保證在URI、列名等項的值發生變化時依然能被正確的操作。
一個契約類對開發人員有協助,因為它提供了一個好識別的名字,而不用直接使用數字,於是開發人員就不會為列名或URI們使用錯誤的值。因為它是一個類,它就可以包含Javadoc文檔。整合式開發環境,比如Eclipse可以跟據契約類自動完成常量名並為常量顯示Javadoc。
開發人員不能從你的應用操作契約類的類檔案,但是他們可以從你提供的.jar檔案中靜態把它編譯到他們的應用中 。
ContactsContract 類和它的嵌套類們就是契約類的例子。
實現Content Provider 許可權
對android系統的所有方面的許可權在主題Security and Permissions中有詳細的描述。主題 Data Storage 中也描術了安全和許可權對各種儲存類型的影響。簡略的說,重要的幾點是:
· 預設下,儲存在裝置的內部儲存空間上的資料檔案是你的應用和provider私人的。
· 你建立的SQLiteDatabase 資料庫是你的應用和provider私人的。
· 預設上,儲存到外部儲存空間上的資料檔案是公有的和全域可讀的。你不能使用一個content provider來限制外部儲存上的檔案的操作,因為其它應用可以使用其它API 來讀寫它們。
· 那些用於在你的裝置內部儲存空間上開啟或建立檔案或SQLite資料的方法們可能會暗中給予其它應用讀寫的許可權。如果你使用一個內部檔案或資料庫作為你的provider的資料倉儲,並且你給予它們"world-readable" 或"world-writeable" 許可權,你在manifest中對你的provider的許可權聲明將不能保護你的資料。內部儲存上的檔案和資料庫的的預設許可權是"private",並且你也不應該改變你的provider的資料倉儲的許可權。
如果你想使用content provider 的許可權來控制對你的資料的操作,那麼你應該儲存你的資料到內部檔案,SQLite 資料庫,或"cloud" (例如,在一個遠程服務上),並且你應該保持檔案和資料庫私屬於你的應用。
實現許可權
所有的應用都可以從你的provider讀或寫你的provider寫,即使背景資料是私人的。因為預設下你的provider 沒有使用權限設定。要改變這種情況,可以在manifest檔案中設定你的provider的許可權,使用<provider> 元素的屬性或其兒子項目。你可以設定應用於整個provider的許可權,或只應用於特定表的,或特定記錄的,或所有三者的。
你在manifest檔案中用一個或多個<permission> 元素定義你的provider的許可權。要使用於你的provider的許可權是唯一的,為使用android:name屬性使用Java風格的範圍限定。例如, 為讀許可權命名為com.example.app.provider.permission.READ_PROVIDER.
下面所列的描述說明了provider許可權的範圍,開始是應用於整個provider的然後變成更細顆粒度。更細顆粒度的許可權優先順序高於大範圍的許可權:
單一的read-write provider-level許可權
一個許可權,它控制對整個provider的讀和寫入權限,用<provider> 元素的android:permission 屬性指定。
分開的讀和寫provider-level許可權
控制整個provider的一個讀許可權和一個寫入權限。你用<provider> 元素的android:readPermission和android:writePermission屬性指定它們。它們的優先順序比android:permission更高。
Path-level 許可權
對你的provider中的content URI的讀、寫,或讀/寫入權限。你使用<provider> 元素<path-permission> 子項目指定的指定每個URI的許可權,你可以指定一個讀/寫入權限,一個讀許可權,或一個寫入權限,或所有三者。讀和寫入權限優先順序高於讀/寫入權限。並且,path-level許可權優先順序高於 provider-level許可權。
臨時許可權
也是一個權限等級,它代表了臨時擷取並賦於一個應用的許可權,即使這個應用不具有許可權。臨時許可權特性減少了一個應用需要在其mainifest中聲明的許可權的數量。當你開啟了臨時許可權,只有那些持續操作你的所有資料的應用們才需要對你的provider有“持久的”許可權.
考慮一下你實現一個email provider和應用,當你想允許一個外部映像查看應用通過你的provider來顯示映像附件時所需的許可權。要想不用聲明許可權就給予映像查看應用所需的許可權,就需設定映像的content URI的臨時許可權。這樣設計你的email 應用:當使用者想要顯示一個映像,你的應用發送一個intent給映像查看應用,這個intent包含了映像的content URI 和許可權。映像查看應用之後可以請求你的email provider來擷取映像,即使映像查看應用對你的provider不具有普通的讀許可權。
要開啟臨時許可權,既可以設定<provider>元素的android:grantUriPermissions 屬性,也可以添加一個或多個 <grant-uri-permission> 子項目到你的<provider> 元素。如果你使用臨時許可權,你必須在從你的provider刪除對一個content URI的支援時調用Context.revokeUriPermission() ,如果這個content URI關聯了一個臨時許可權的話。
屬性的值決定了你的provider具有什麼操作的可操作性。如果屬性被設定為true,那麼系統將為你的整個provider擷取臨時許可權,覆蓋任何其它通過provider-level或path-level 擷取的許可權。
如果這個flag 設定為false, 那麼你必須添加<grant-uri-permission> 子項目到你的<provider> 元素上。每個子項目指定了哪個content URI 或哪些URI們授予了臨時許可權。
要把臨時許可權委託給一個應用,intent必須包含FLAG_GRANT_READ_URI_PERMISSION 或FLAG_GRANT_WRITE_URI_PERMISSION 標誌,或兩者都有。它們可用setFlags() 方法設定。
如果android:grantUriPermissions 屬性不存在,就被認為是false。