Android FileProvider 使用指南
FileProvider是 Android 7.0+ 系统中安全的文件分享机制,用于替代传统的 file:// URI 方式。
1、配置FileProvider
要在项目中引入 FileProvider,只需要在 build.gradle 文件中添加 AndroidX 核心库依赖即可(通常新建项目时已默认包含)。
1.1、添加依赖
在模块级(Module)的 build.gradle 文件中加入以下代码:
dependencies {
// FileProvider 包含在 AndroidX Core 库中
implementation "androidx.core:core:1.15.0"
}
1.2、在AndroidManifest中注册
引入库后,必须在 <application> 标签内配置 Provider:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
1.3、创建路径配置文件
在 res/xml 目录下新建 file_paths.xml 文件,定义允许共享的路径:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<root-path name="root" path="."/> <!-- 谨慎使用 -->
<!-- 代表 Context.getExternalFilesDir(null) 目录 -->
<external-files-path name="my_images" path="images/" />
</paths>
多目录配置,可以在 file_paths.xml 中配置多个路径:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="schemas.android.com">
<!-- 对应 Context.getFilesDir():应用内部私有存储根目录 -->
<files-path
name="internal_files"
path="." />
<!-- 对应 Context.getCacheDir():应用内部私有缓存目录 -->
<cache-path
name="internal_cache"
path="." />
<!-- 对应 Environment.getExternalStorageDirectory():SD卡根目录 (Android 11+ 建议慎用) -->
<external-path
name="external_storage"
path="." />
<!-- 对应 Context.getExternalFilesDir(null):外部存储私有文件目录 -->
<external-files-path
name="ext_files"
path="images/" />
<!-- 对应 Context.getExternalCacheDir():外部存储私有缓存目录 -->
<external-cache-path
name="ext_cache"
path="videos/" />
</paths>
在 FileProvider 的 XML 路径配置中,path="":无效配置,会导致 FileProvider 无法正常工作。path="." 代表当前目录,即指定的根路径本身。
<files-path name="internal_files" path="." />
这是 FileProvider 路径配置中的一个特殊写法,具有特定用途:<files-path> 对应的基目录是 Context.getFilesDir()(应用内部存储目录)path="." 表示允许访问这个基目录下的所有内容
1.4、不同标签的实际路径对照*
| XML 标签 | 对应代码中的路径方法 | 物理路径示例 (以包名 com.example 为例) |
|---|
<files-path /> | context.getFilesDir() | /data/user/0/com.example/files |
<cache-path /> | context.getCacheDir() | /data/user/0/com.example/cache |
<external-path /> | Environment.getExternalStorageDirectory() | /storage/emulated/0/ (SD卡根目录) |
<external-files-path /> | context.getExternalFilesDir(null) | /storage/emulated/0/Android/data/com.example/files |
<external-cache-path /> | context.getExternalCacheDir() | /storage/emulated/0/Android/data/com.example/cache |
<external-media-path /> |
2、使用FileProvider
注意:使用 getUriForFile() 获取 files-path 下文件的 Uri 并发送 Intent 时,必须调用 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION),否则接收方应用会因为没有读取私有目录的权限而崩溃。
2.1、分享文件
使用 getUriForFile() 获取 files-path 下文件的 Uri 进行分享:
fun shareFile(context: Context, file: File) {
val uri = FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
file
)
val shareIntent = Intent().apply {
action = Intent.ACTION_SEND
type = getMimeType(file.path)
putExtra(Intent.EXTRA_STREAM, uri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(Intent.createChooser(shareIntent, "分享到"))
} 支持多种文件类型,根据文件后缀设置正确的 MIME Type:
fun getMimeType(path: String): String {
return when (path.substringAfterLast('.')) {
"pdf" -> "application/pdf"
"jpg", "jpeg" -> "image/jpeg"
"png" -> "image/png"
else -> "*/*"
}
}
2.2、拍照并保存照片
调用系统相机拍摄一张照片,并将其保存到当前应用的私有缓存目录中。
fun takePhoto(context: Context) {
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val photoFile = File(context.externalCacheDir, "${System.currentTimeMillis()}.jpg")
val photoUri = FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
photoFile
)
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri) //告诉相机“存哪里”
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
startActivityForResult(intent, REQUEST_CODE)
}
2.3、临时权限控制
属于进阶技巧,授予目标包名读取权限:
// 授予目标包名读取权限
context.grantUriPermission(
targetPackageName,
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
撤销所有权限
// 撤销所有权限
context.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
FileProvider 是 Android 安全体系的重要组成部分,合理使用可以有效提升应用的安全性。确认所有文件分享都使用了 FileProvider,不再直接使用 file:// URI