1.問題
Android7.0開始,應用私有目錄被限制訪問,官方做了如下限制:
1.私有文件的文件權限不應再由所有者放寬,使用MODE_WORLD_READABLE/MODE_WORLD_WRITEABLE將拋出異常
2.嚮應用外傳遞file://URI會出發FileUriExposedException
2.FileProvider
當targetSdkVersion>=24時,會存在上述問題,可能涉及到的場景有:拍照,程序安裝等。
同時,官方在v4包(api=22開始)中引入FileProvider類用於程序間私有文件的共享。該類繼承自ContentProvider,使用時需要在清單文件中註冊。
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.servertest.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
</provider>
設置可訪問的共享文件
FileProvider只能對聲明的文件夾下的文件生成uri,該文件夾的聲明是在xml中使用標籤完成的,下面的例子就是聲明私有文件目錄下images/下的文件可以臨時訪問(文件在res/xml/目錄下),下面時一個簡單的樣式:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
</paths>
因爲的子標籤可以有多種,這裏對所有進行說明:
子標籤中屬性說明:
Android7.1.1的路徑:
files-path
Context.getFilesDir():
/data/user/0/包名/files
cache-path
Context.getCacheDir():
/data/user/0/包名/cache
external-path
Environment.getExternalStorageDirectory():
/storage/emulated/0
external-files-path
Context.getExternalCacheDir():
/storage/emulated/0/Android/data/包名/cache
Android4.4的路徑:
file-path
Context.getFilesDir():
/data/data/包名/files
cache-path
Context.getCacheDir():
/data/data/包名/cache
external-path
Environment.getExternalStorageDirectory():
/storage/sdcard
external-files-path
Context.getExternalCacheDir():
/storage/sdcard/Android/data/包名/cache
以上便是Android官方文檔上介紹的FileProvider所有支持的所以path類型,這些類型在Android手機內部存儲區文件共享是可以行的通的,但對於外置SD卡是不行的,如果你想通過FileProvider.getUriForFile()獲取一個外置SD卡的Uri則會報出如下異常:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<root-path name="my_images" path=""/>
</paths>
root-path代表/也就是Android設備的根目錄,該目錄下包含着手機內部存儲器,外置SD卡等所有文件的目錄。
然後我們允許程序,發現將path設置爲root-path解決了FileProvider無法使用外置SD卡的問題。
完成配置
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.servertest.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
說明:
name:爲固定值android.support.FILE_PROVIDER_PATHS
resource:所對應的xml文件路徑
使用
1、通過路徑生成要分享的文件File對象
2、使用FileProvider生成uri—FileProvider.getUriForFile()
authrity爲”com.example.servertest.fileprovider”的FileProvider,獲取到文件”image.jpg”文件,該文件位於”images”目錄下
File imagesPath = new File(getFilesDir(), "images");
File newFile = new File(imagesPath, "picture.jpg");
Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
沒被封裝的uri爲:
/data/user/0/com.example.cameraalbumtest/files/images/picture.jpg
封裝後的uri爲:
content://com.example.cameraalbumtest.fileprovider/my_images/picture.jpg
com.example.cameraalbumtest.fileprovider: 這個是provider的標識符
my_images: 就是file-path 它代表的路徑就是/data/user/0/com.example.cameraalbumtest/files/images
臨時權限的授予方式
1、使用Context.grantUriPermission(package,Uri,mode_flags)方法,使用想要的模式。這個方法通過mode_flags方法授予客戶端package的臨時權限,有兩個取值,FLAG_GRANT_URI_PERMISSION和FLAG_GRANT_WRITE_URI_PERMISSOIN。該方式允許後,可通過revokeUriPermission終止,或者手機重啓後
2、通過Intent
- 通過Intent的setData()方法將該uri放入Intent中
- 可以爲Intent設置flag,設置一個或兩個, FLAG_GRANT_URI_PERMISSION和FLAG_GRANT_WRITE_URI_PERMISSOIN
- 將Intent發送給其他app,大部分情況,通過setResult()來做這種方法獲取的權限,當接收的Activity在棧中一直活躍時都會保留,當activity棧finish時,權限會自動移除。被允許的activity所在的app的其他組件也會被允許該權限。