You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部分安卓设备(如三星)Bitmap无法保存至相册,求代码优化方案

三星设备Bitmap保存相册适配方案

以下是针对现有代码的优化点,覆盖三星设备绝大多数存图失败的场景:


核心优化点

  • 修复文件名含冒号的问题:三星系统不允许文件名携带:字符,原有时间格式化规则yyyy-MM-dd_HH:mm:ss包含冒号,直接替换为下划线即可
  • 修正MIME类型为标准值image/jpeg,避免三星MediaStore对非标准类型的拦截
  • 补充Android 10+的IS_PENDING状态重置逻辑,写入完成后告知系统文件已就绪,否则三星系统不会将图片展示到相册
  • 补充空值校验,避免插入uri为空时触发空指针崩溃
  • 修复Bitmap参数校验漏洞,避免数组越界
  • 低版本增加相册扫描广播,解决三星低版本系统不会自动刷新相册的问题
  • 用try-with-resources自动管理流,避免资源泄漏

优化后代码

protected Void doInBackground(Bitmap... data) {
    // 先校验参数合法性,避免数组越界
    if (data.length == 0 || data[0] == null || data[0].isRecycled()) return null;
    
    ContentResolver resolver = mContext.getContentResolver();
    // 替换时间格式中的冒号为下划线,指定Locale避免系统语言差异异常
    String fileName = "XF_" + new SimpleDateFormat("yyyy-MM-dd_HH_mm_ss", Locale.getDefault()).format(new Date()) + ".jpg";
    String appDirName = mContext.getString(R.string.app_name);
    ContentValues values = new ContentValues();
    values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
    // 修正为标准JPG MIME类型
    values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
    
    Uri uri = null;
    File targetFile = null;

    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + appDirName);
            values.put(MediaStore.MediaColumns.IS_PENDING, 1);
            uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        } else {
            // 路径和高版本对齐,存到PICTURES下的应用目录
            File directory = new File(Environment.getExternalStorageDirectory(), 
                    Environment.DIRECTORY_PICTURES + File.separator + appDirName);
            if(!directory.exists()){
                directory.mkdirs();
            }
            targetFile = new File(directory, fileName);
            values.put(MediaStore.MediaColumns.DATA, targetFile.getAbsolutePath());
            uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        }

        if (uri == null) {
            Log.e("myApp", "插入MediaStore失败,uri为空");
            return null;
        }

        // try-with-resources自动关流
        try (OutputStream output = resolver.openOutputStream(uri)) {
            if (output == null) return null;
            data[0].compress(Bitmap.CompressFormat.JPEG, 100, output);
            output.flush();
        }

        // 重置IS_PENDING状态,告知系统写入完成
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.clear();
            values.put(MediaStore.MediaColumns.IS_PENDING, 0);
            resolver.update(uri, values, null, null);
        } else if (targetFile != null) {
            // 低版本发送广播刷新相册
            mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(targetFile)));
        }
        Log.d("myApp", "图片保存成功");

    } catch (IOException e) {
        Log.e("myApp", "图片保存失败:" + e.getMessage());
        e.printStackTrace();
        // 插入失败时如果是Android10+要删除已创建的空条目
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && uri != null) {
            resolver.delete(uri, null, null);
        }
    } catch (Exception e) {
        Log.e("myApp", "图片保存失败:" + e.getMessage());
        e.printStackTrace();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && uri != null) {
            resolver.delete(uri, null, null);
        }
    }
    return null;
}

额外注意事项

  • Android 9及以下版本必须提前申请WRITE_EXTERNAL_STORAGE运行时权限,三星对存储权限管控严格,未授权会直接写入失败
  • 不要直接复用已经调用过recycle()的Bitmap,避免压缩失败

内容的提问来源于stack exchange,提问作者Patola

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 18:27:07