實現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。