Android FileProvider 使用指南 四大组件

Quibbler 1月前 105

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

不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com