Android7.0之后請使用FileProvider
在官方7.0的以上的系統中,嘗試傳遞 “file:/// Uri”可能會觸發FileUriExposedException。FileProvider是 ContentProvider的子類。它的作用是通過創建“content:// Uri ”的方式來實現文件的安全共享。
我們可以授予臨時訪問權限(只讀、只寫或兩者)給一個content URI。授予臨時權限的最常見的方式是調用Intent.setFlags()。把這個Intent發送給其他app,讓它們可以共享此文件。而授予的權限的有效性,只要接收Intent的Activity是active狀態時,權限就會一直有效。如果這個Intent是發給一個Service,那么只要Service是運行狀態權限就是有效的。
”file:/// Uri“這種方式訪問文件是不安全的。首先這種方式訪問文件亦需要權限,即也存在授予臨時權限的過程,但是這種對訪問的控制的方式必須修改文件系統底層文件的權限來實現。這種方式授予的臨時權限會變得對任何一個app都可用的(“愛泛濫”),而且權限會一直有效,直到你改變它為止或者重啟手機設備。在Android7.0之后,就通過FileProvider的方式,進一步約束我們的權限泛濫的范圍,避免出現權限泄漏的問題。
使用FileProvider的步驟:
步驟1:在AndroidManifest.xml定義FileProvider
1
2
3
4
5
6
7
8
9
10
11
12
13
14
FileProvider已經提供了很多默認的功能,包括為文件生成content URI,所以我們沒有必要再去重新定義子類,如果你想修改它的默認行為,可以繼承FileProvider寫個子類。
android:name 屬性設置 為androidx.core.content.FileProvider或android.support.v4.content.FileProvider。類名必須是全路徑的。
android:authorities屬性設置為一個類似域名的字符串。到時這個URI授權字符串,就是用來授權用的,它將成為content Uri的路徑的一部分,就像網站的域名。
android:exported屬性設置為false,因為它沒有必要公開。
android:grantUriPermissions屬性設置為true,這樣才會允許你授予對文件的臨時訪問權限
步驟2:指定可用的文件
FileProvider只能為事先指定的目錄里的文件生成content URI。 所以這一步我們就要指FileProvider可以為哪目錄下的文件生成Content URI。這些目錄可以通過在values/xml目錄創建一份xml文件,然后在里面指定,如創建files_path.xml,在文件里加上相應的目錄路徑:
1
2
3
4
它的語法格式:
1
outsideName的值是路徑的一部分,它是用了隱藏真實的路徑的。
actualPath才是真實的路徑。
最后,要在AndroidManifest.xml文件的provider標簽里用meta-data來指定:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
步驟3:牛刀小試,為一個文件生成Content URI
通過content URI與另一個app共享一份文件前,我們的app必須生成要共享文件的content URI。為這個共享文件生成一個新的File實例。然后把file實例傳給getUriForFile(),它就會為你返回一個content URI了。
返回的content URI可以被放在一個Intent中發給另一個app。收到這個Intent的app就可以通過調用ContentResolver.openFileDescriptor獲得一個ParcelFileDescriptor(包文件描述符)來打開文件并訪問文件的內容。
File filePath = new File(Environment.getExternalStorageDirectory(), "aaa"); File file = new File(filePath, "index.html"); Uri uri = FileProvider.getUriForFile(getApplicationContext(),"com.mydomain.fileprovider",file); //content://com.mydomain.fileprovider/files/index.html
1
2
3
4
步驟4:但是在發送Intent前,要先授予臨時權限給content URI
授予content URI臨時權限有以下幾種方式:
(1)調用Context.grantUriPermission(package, Uri, mode_flags):
package:還指明臨時權限授予的包。
Uri:包含content URI
mode_flags:可以設置Intent.FLAG_GRANT_READ_URI_PERMISSION(只讀權限), Intent.FLAG_GRANT_WRITE_URI_PERMISSION(只寫權限)或者兩者。這種方式授予的權限會一直起作用直到你撤銷或重啟手機設備為止才消失。
(2)通過調用setData()將content URI放在Intent中
(3)調用Intent.setFlags() :值可以填Intent.FLAG_GRANT_READ_URI_PERMISSION或Intent.FLAG_GRANT_WRITE_URI_PERMISSION或兩者。
最后,就可以將這個Intent發給其他app,讓它們共享這個文件了。
通過Intent授予的臨時權限,只要接收Intent的Activity是活動(active狀態)的,那么權限就會一直生效。當Activity所在的棧結束時,權限就會被自動移除。臨時權限授予給某個app的Activity,這個權限會擴展到它所在的整個app,只有這個app棧結束時,權限才會被移除。
步驟5:提供Content URI給其他App
最常用的方式是調用startActivityResult(),這種方式就是發送一個Intent給你自己的app去打開另一個Activity。作為回應,我們的app會馬上返回一個content URI給客戶端app或者呈現一個允許我們選擇文件用戶界面。對于后者,我們一旦選擇了某個文件,我們的app就會返回它的content URI。但兩者兩者最后都會通過setResult()的方式返回一個包含了content URI的Intent。
我們還可以調用Intent.setClipData()將content URI放到ClipData對象中,并將此對象添加到一個Intent中,再發送給其他app。使用這種方法,我們可以添加很多ClipData對象到Intent中,每一個對象都同它自己的content URI綁定。當我們使用Intent.setFlags()這種方式設置臨時訪問權限時,這些權限會應用到所有的content URI上。
Demo已上傳GitHub,歡迎下載學習交流。
謝謝閱讀
Android XML
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。