如何在 Android 中通过返回的 Uri 保存背景去除后的图像

冬明酱_3603

冬明酱_3603

2026-03-24

725人浏览

原创

如何在 Android 中通过返回的 Uri 保存背景去除后的图像

本文详解如何在使用 CutOut 等图像处理库(如背景去除)后,正确利用 Uri 将处理结果保存至设备存储,涵盖 Bitmap 获取、输出流写入、权限适配及常见陷阱规避。

本文详解如何在使用 cutout 等图像处理库(如背景去除)后,正确利用 `uri` 将处理结果保存至设备存储,涵盖 bitmap 获取、输出流写入、权限适配及常见陷阱规避。

在 Android 开发中,调用第三方图像处理库(如 CutOut)完成背景去除后,通常会通过 onActivityResult 接收一个 Uri,该 Uri 指向临时生成的处理结果图像。但该 Uri 并非可直接写入的目标路径——它可能是 content:// 协议的临时内容 URI(如来自 MediaStore 或沙盒临时文件),不能直接用于 FileOutputStream;若强行拼接 file:// 路径(如原答案中的 file:// + Environment.getExternalStorageDirectory()),不仅违反 Android 10+ 的分区存储(Scoped Storage)规范,还会在 Android 7.0+ 因 FileProvider 限制导致 FileUriExposedException。

✅ 正确做法是:以目标 Uri 为写入终点,通过 ContentResolver.openOutputStream() 获取安全、兼容的输出流。以下是推荐实现(已适配 Android 10+):

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == CutOut.CUTOUT_ACTIVITY_REQUEST_CODE) {
        switch (resultCode) {
            case Activity.RESULT_OK:
                Uri imageUri = CutOut.getUri(data);
                if (imageUri != null) {
                    saveImageFromUri(imageUri);
                }
                startActivity(new Intent(this, MainActivity.class));
                break;
            case CutOut.CUTOUT_ACTIVITY_RESULT_ERROR_CODE:
                Exception ex = CutOut.getError(data);
                Log.e("CutOut", "Error during background removal", ex);
                break;
            default:
                Log.d("CutOut", "User cancelled");
                startActivity(new Intent(this, AdvanceFeatures.class));
        }
    }
}

private void saveImageFromUri(Uri sourceUri) {
    try {
        // 1. 从 sourceUri 加载 Bitmap(自动适配 content:// 和 file://)
        Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), sourceUri);

        // 2. 构造目标保存 URI:推荐使用 MediaStore 插入新条目(兼容 Scoped Storage)
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, "bg_removed_" + System.currentTimeMillis() + ".png");
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
        // ⚠️ 注意:Android 10+ 必须指定相对目录(如 DCIM、Pictures),否则插入失败
        values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/CutOutResults");

        Uri insertUri = getContentResolver().insert(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            values
        );

        if (insertUri != null) {
            // 3. 打开输出流并写入压缩后的图像
            try (OutputStream os = getContentResolver().openOutputStream(insertUri)) {
                bitmap.compress(Bitmap.CompressFormat.PNG, 95, os); // 95 是平衡质量与体积的推荐值
            }
            // 4. 触发媒体扫描(可选,确保相册立即可见)
            sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, insertUri));
            Toast.makeText(this, "图像已保存至相册 → CutOutResults 文件夹", Toast.LENGTH_SHORT).show();
        } else {
            throw new IOException("Failed to insert image into MediaStore");
        }

    } catch (IOException e) {
        Log.e("SaveImage", "Failed to save image from URI", e);
        Toast.makeText(this, "保存失败:" + e.getMessage(), Toast.LENGTH_LONG).show();
    }
}

? 关键注意事项:

  • ✅ 不要硬编码 file:// 路径:自 Android 7.0 起禁止暴露 file:// URI 给其他应用;自 Android 10 起外部私有目录不可直接访问。
  • ✅ 优先使用 MediaStore 写入:MediaStore.Images.Media.EXTERNAL_CONTENT_URI 是 Android 官方推荐的、符合分区存储规范的持久化方式。
  • ✅ 动态申请存储权限:Android 6.0+ 需在运行时请求 Manifest.permission.READ_EXTERNAL_STORAGE(仅读取)或 WRITE_EXTERNAL_STORAGE(仅 Android 9 及以下);Android 10+ 无需 WRITE_EXTERNAL_STORAGE,但需确保 targetSdkVersion >= 30 且未声明 requestLegacyExternalStorage=true。
  • ✅ 异常处理必须完备:getBitmap() 和 openOutputStream() 均可能抛出 IOException,务必捕获并反馈用户。
  • ✅ Bitmap 内存管理:大图建议先采样缩放(BitmapFactory.Options.inSampleSize),避免 OOM。

? 进阶提示:若需保存至 App 私有目录(不暴露给相册),可改用 getExternalFilesDir(Environment.DIRECTORY_PICTURES) + FileOutputStream,此路径无需权限且始终可用,但文件仅本应用可访问。

综上,保存 Uri 图像的本质是「解码 → 编码 → 安全写入」,核心在于尊重 Android 存储演进策略。采用 MediaStore 插入方案,既保障兼容性,又符合现代最佳实践。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
android开发三大框架
android开发三大框架

android开发三大框架是XUtil框架、volley框架、ImageLoader框架。本专题为大家提供android开发三大框架相关的各种文章、以及下载和课程。

2023.08.14

902

4

android是什么系统
android是什么系统

Android是一种功能强大、灵活可定制、应用丰富、多任务处理能力强、兼容性好、网络连接能力强的操作系统。本专题为大家提供android相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.22

2687

5

android权限限制怎么解开
android权限限制怎么解开

android权限限制可以使用Root权限、第三方权限管理应用程序、ADB命令和Xposed框架解开。详细介绍:1、Root权限,通过获取Root权限,用户可以解锁所有权限,并对系统进行自定义和修改;2、第三方权限管理应用程序,用户可以轻松地控制和管理应用程序的权限;3、ADB命令,用户可以在设备上执行各种操作,包括解锁权限;4、Xposed框架,用户可以在不修改系统文件的情况下修改应用程序的行为和权限。

2023.09.19

5197

7

android重启应用的方法有哪些
android重启应用的方法有哪些

android重启应用有通过Intent、PendingIntent、系统服务、Runtime等方法。本专题为大家提供Android相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.18

665

5

Android语音播放功能实现方法
Android语音播放功能实现方法

实现方法有使用MediaPlayer实现、使用SoundPool实现两种。可以根据具体的需求选择适合的方法进行实现。想了解更多语音播放的相关内容,可以阅读本专题下面的文章。

2024.03.01

964

5

Playwright 测试失败怎么办
Playwright 测试失败怎么办

Playwright测试失败专题汇集超时、定位器匹配、浏览器提前关闭、依赖与插件配置、文件上传、SSL及Docker启动等故障文章,并介绍通过截图、报告和Trace定位错误。

2026.10.21

20

15

Playwright 页面操作指南
Playwright 页面操作指南

Playwright页面操作专题汇集元素定位、点击与输入、等待响应、多页面切换、Iframe、事件监听及文件处理的已审核文章,便于按操作场景查阅示例与常见问题。

2026.10.21

0

16

Playwright 入门教程
Playwright 入门教程

Playwright入门专题汇集测试框架安装、VS Code配置、录制脚本、编写首个用例、运行调试及查看报告的已审核文章,帮助初学者搭建端到端测试流程。

2026.10.21

0

16

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

2026.10.16

120

10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.9万人学习