android 7.0 因为file://引起的FileUriExposedException异常

 private void dialog() {
        SheetDialog sheetDialog = new SheetDialog(this);
        sheetDialog.setItemClickListener(new SheetDialog.OnSheetDialogListener() {
            @Override
            public void onItemClick(int id, int position) {
                switch (id) {
                    case SheetDialog.TYPE_TAKE_PICTURE:
                        if (PermissionUtils.lacksPermissions(ChatReportActivity.this,
                                PERMISSIONS)) {
                            // 缺少权限时, 进入权限配置页面
                            PermissionsActivity.startActivityForResult(ChatReportActivity.this,
                                    REQUEST_CODE_ASK_CAMERA_PERMISSION, PERMISSIONS);
                        } else {
                            showCameraAction();
                        }
                        break;
                    case SheetDialog.TYPE_SELECT_PHOTO:
                        if (PermissionUtils.lacksPermissions(ChatReportActivity.this,
                                PERMISSIONS)) {
                            // 缺少权限时, 进入权限配置页面
                            PermissionsActivity.startActivityForResult(ChatReportActivity.this,
                                    REQUEST_CODE_ASK_WRITE_PERMISSION, PERMISSIONS_WRITE);
                        } else {
                            goToSelectPicture();
                        }
                        break;
                }
            }
        });
        sheetDialog.show();
    }
调用相机:
private void showCameraAction() {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        try {
            mTmpFile = FileUtils.createTmpFile(this);
        } catch (IOException e) {
            e.printStackTrace();
        }
        Uri photoOutputUri = FileProvider.getUriForFile(this,
                getPackageName() + ".fileprovider", mTmpFile);
        if (mTmpFile != null && mTmpFile.exists()) {
            intent.putExtra(MediaStore.EXTRA_OUTPUT, photoOutputUri);
            startActivityForResult(intent, REQUEST_CAMERA);
        }
    }

调用相册:

private void goToSelectPicture() {
        Intent intent = new Intent(Intent.ACTION_PICK,
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        intent.setType("image/*");
        startActivityForResult(Intent.createChooser(intent, "选择图片"), REQUEST_GALLERY);
    }
@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // 拒绝时, 关闭页面, 缺少主要权限, 无法运行
        if (requestCode == REQUEST_CODE_ASK_CAMERA_PERMISSION) {
            if (resultCode == PermissionsActivity.PERMISSIONS_DENIED) {
                Toast.makeText(ChatReportActivity.this, "缺少必要权限", Toast.LENGTH_SHORT).show();
            } else {
                showCameraAction();
            }
            return;
        }

        if (resultCode == RESULT_OK) {
            handleImgRequest(requestCode, data);
        }
    }

配置文件需要加入:

      <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.yjtx.fsl.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

然后在,res目录中,新建一个xml文件,然后创建 file_paths.xml文件。

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <!--<external-path path="Android/data/com.yjtx.fsl/" name="files_root" />-->
    <!--<external-path path="." name="external_storage_root" />-->
    <external-path name="external_files" path="."/>
</paths>
转载:https://www.jianshu.com/p/55b817530fa3

<think>首先,用户的问题是:"android.os.FileUriExposedException file Intent.getData() solution",意思是寻求解决AndroidFileUriExposedException异常的方案。这个异常发生在尝试通过Intent暴露文件URI时,特别是在Android 7.0(API 24)及以上版本。 从系统级指令: - 所有行内数学表达式必须使用$...$格式,但这里没有数学内容,所以不需要。 - 独立公式必须使用$$...$$格式,但同样,没有数学公式。 - LaTeX语法正确:确保任何LaTeX正确,但这里可能不涉及。 - 使用中文回答:整个响应必须用中文。 - 生成相关问题:在回答结束时,生成相关问题。 - 添加引用标识:在引用的段落末尾自然地添加引用标识,如[^1]。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 参考站内引用: - 引用[1]到[4]是关于FileUriExposedException的示例错误信息和一些背景,但用户意图是解决这个异常。所以,我需要基于此提供解决方案。 关键点:FileUriExposedException是由于在Android 7.0及以上,使用file:// URI暴露文件给其他应用导致的。解决方案是使用FileProvider来安全共享文件。 结构化回答: 1. **解释问题**:简要说明异常的原因。 2. **提供解决方案**:逐步指导如何实现FileProvider。 3. **代码示例**:提供代码片段来演示。 4. **注意事项**:提到任何额外考虑。 5. **生成相关问题**:在结束时,列出相关问题。 用中文回答: - 整个响应用中文撰写。 生成相关问题: - 基于主题,生成2-3个相关问题。 添加引用: - 在回答中,如果引用参考内容,添加引用标识。但参考内容只是示例错误,我可能需要基于一般知识回答。所以,自然地添加引用,比如在解释错误时引用[^1]或[^2]。 最终响应结构: - 开头:介绍问题。 - 主体:逐步解决方案。 - 代码块:用代码示例。 - 结尾:生成相关问题。</think>### Android FileUriExposedException 解决方案 在 Android 开发中,`android.os.FileUriExposedException` 异常通常发生在应用尝试通过 `Intent.getData()` 暴露 `file://` URI 时,尤其是在 Android 7.0 (API 24) 及更高版本上。这是因为 Google 引入了更严格的文件共享安全策略,禁止直接使用 `file://` URI 跨应用共享文件,以防止潜在的安全漏洞[^1][^2]。以下是一个结构化的解决方案,帮助您逐步修复此问题。解决方案基于官方推荐的 `FileProvider` 机制,它使用 `content://` URI 替代 `file://` URI 来实现安全文件共享。 #### 步骤 1: 理解问题原因 - **异常触发场景**:当您的应用通过 `Intent` (如启动相机、分享文件或打开文件) 传递 `file://` URI 时,系统会检测到 URI 暴露给其他应用,从而抛出 `FileUriExposedException`。错误日志通常类似于: ``` android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData() ``` 这表示 URI 被不安全地暴露[^1][^2]。 - **根本原因**:Android 7.0 及以上版本执行了 **StrictMode** 策略,禁止跨应用直接访问 `file://` URI。必须改用 `FileProvider` 生成安全的 `content://` URI[^2]。 #### 步骤 2: 实现 FileProvider 解决方案 `FileProvider` 是 Android Support Library 或 AndroidX 的一部分,它允许您安全地共享文件。以下是详细步骤: 1. **添加依赖项** 在项目的 `build.gradle` 文件中,确保已添加 AndroidX 库依赖(如果尚未添加): ```gradle dependencies { implementation 'androidx.core:core-ktx:1.7.0' // 或更高版本 } ``` 如果使用旧版 Support Library,使用 `com.android.support:support-v4:28.0.0`,但推荐迁移到 AndroidX。 2. **在 AndroidManifest.xml 中声明 FileProvider** 在 `<application>` 标签内添加 `FileProvider` 的 `<provider>` 配置。指定一个唯一的 `authorities` 属性(通常使用应用包名),并定义文件共享路径: ```xml <application> <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> </application> ``` - `authorities`:替换 `${applicationId}` 为您的应用包名(如 `com.example.app.fileprovider`)。 - `exported="false"`:确保 FileProvider 不对外公开,仅限应用内部使用。 - `grantUriPermissions="true"`:允许临时 URI 权限。 - `meta-data`:指向一个 XML 文件,定义共享文件的路径。 3. **创建文件路径配置文件 (file_paths.xml)** 在 `res/xml/` 目录下创建 `file_paths.xml` 文件(如果目录不存在,请手动创建)。此文件定义哪些目录的文件可被共享。例如: ```xml <?xml version="1.0" encoding="utf-8"?> <paths> <!-- 共享外部存储根目录 --> <external-path name="external_files" path="." /> <!-- 共享应用缓存目录 --> <cache-path name="cache_files" path="/" /> <!-- 共享应用内部存储目录 --> <files-path name="internal_files" path="/" /> </paths> ``` - `<external-path>`:对应 `Environment.getExternalStorageDirectory()`。 - `<cache-path>`:对应 `getCacheDir()`。 - `<files-path>`:对应 `getFilesDir()`。 - `name` 属性是 URI 路径的别名,`path` 指定子目录(`.` 表示根目录)。 4. **在代码中使用 FileProvider 生成 URI** 修改您的代码,将 `file://` URI 替换为 `FileProvider` 生成的 `content://` URI。以下是一个示例,展示如何安全地启动相机 Intent: ```java import androidx.core.content.FileProvider; import java.io.File; public class MainActivity extends AppCompatActivity { private static final int REQUEST_IMAGE_CAPTURE = 1; private Uri photoUri; private void takePicture() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { // 创建临时文件 File photoFile = new File(getExternalCacheDir(), "photo.jpg"); try { if (photoFile.createNewFile()) { // 使用 FileProvider 获取 URI photoUri = FileProvider.getUriForFile( this, getPackageName() + ".fileprovider", // 与 Manifest 中的 authorities 一致 photoFile ); // 添加 URI 权限并启动 Intent takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); } } catch (IOException e) { e.printStackTrace(); } } } } ``` - **关键点**: - `FileProvider.getUriForFile()` 生成安全的 `content://` URI。 - `addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)` 授予接收方临时读取权限。 - 确保 `authorities` 参数与 `AndroidManifest.xml` 中的配置完全匹配。 #### 步骤 3: 测试和注意事项 - **测试环境**:在 Android 7.0+ 设备或模拟器上运行应用,验证是否不再抛出异常。 - **常见错误处理**: - 如果出现 `IllegalArgumentException`,检查 `file_paths.xml` 中的路径是否正确。 - 对于 Android 10 (API 29) 及以上,还需处理 [Scoped Storage](https://developer.android.com/training/data-storage),优先使用 `MediaStore` 或 `SAF` 共享文件。 - 确保所有文件操作在外部存储上有权限(如 `READ_EXTERNAL_STORAGE`)。 - **性能与兼容性**:`FileProvider` 是轻量级解决方案,兼容 Android 4.1+。它在后台处理 URI 转换,不影响应用性能。 #### 总结 通过以上步骤,您可以使用 `FileProvider` 替代 `file://` URI,避免 `FileUriExposedException`。这符合 Android 的安全最佳实践,确保文件共享在跨应用场景下可靠运行[^1][^2]。如果您遇到具体错误,请提供更多上下文以便进一步调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值