SAF是4.4(API 19)开始使用的数据访问机制。包含以下几个部分:
1. Document Provider:是DocumentsProvider的字类,用来让存储设备显示内容,Android自带Downloads,Images,Videos等这几种。
2. 客户端app:使用ACTION_OPEN_DOCUMENT和ACTION_CREATE_DOCUMENT的intent以及获取返回内容
3. picker:选择文件的界面。
每个document provider包含一个或多个roots,一个root拥有一个独特的COLUMN_ROOT_ID,指向了单个文件或者目录,而这个文件可以指向n个文件,而每个文件又可以指向n个文件。
V4包中的FileProvider这个类可以为文件生成对应的content URI。
1. 定义FileProvider
使用FileProvider首先需要在Manifest文件中定义,其中需要指明生成content URI的权限名称,以及一个指定了可分享目录的xml文件。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application
...>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
...
</application>
</manifest>
其中android:authorities
属性需要指定为包名+.fileprovider,meta-data标签中resource的值@xml/filepaths是指res/xml/filepath.xml文件。该文件包含了可分享目录的路径。
res/xml/filepath.xml的格式如下:
<paths>
<files-path path="images/" name="myimages" />
</paths>
其中<files-path/>
标签用来指定分享的根目录是app内部存储的files/目录,path属性指定所要分享的具体目录,示例中为images目录。name属性指定了所生成URI的自定义路径名。
<paths>
标签可以有多个子标签,标签种类可以包括内部存储<files-path>
和外部存储<external-path>
以及内部缓存<cache-path>
。
如果分享了images/目录下的default_image.jpg文件,则会生成对应的URI为
content://com.example.myapp.fileprovider/myimages/default_image.jpg
<activity
android:name=".FileSelectActivity"
android:label="@File Selector" >
<intent-filter>
<action
android:name="android.intent.action.PICK"/>
<category
android:name="android.intent.category.DEFAULT"/>
<category
android:name="android.intent.category.OPENABLE"/>
<data android:mimeType="text/plain"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
确认了要分享的文件后,最好不要使用Uri.fromFile()来生成file://类型的URI,因为这个方法一是要求分享方在4.4及以下时需要有WRITE_EXTERNAL_STORAGE权限。二是要求请求方需要有READ_EXTERNAL_STORAGE权限,一些app可能会没有,比如说Gmail。最好通过FileProvider.getUriForFile()来生成可授予权限的URI并授予权限
File requestFile = new File(mImageFilename[position]);
// Use the FileProvider to get a content URI
try {
fileUri = FileProvider.getUriForFile(
MainActivity.this,
"com.example.myapp.fileprovider",
requestFile);
if (fileUri != null) {
// Grant temporary read permission to the content URI
mResultIntent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Put the Uri and MIME type in the result Intent
mResultIntent.setDataAndType(
fileUri,
getContentResolver().getType(fileUri));
// Set the result
MainActivity.this.setResult(Activity.RESULT_OK,
mResultIntent);
} else {
mResultIntent.setDataAndType(null, "");
MainActivity.this.setResult(RESULT_CANCELED,
mResultIntent);
}
} catch (IllegalArgumentException e) {
Log.e("File Selector",
"The selected file can't be shared: " +
clickedFilename);
}