部分安卓设备(如三星)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
相关产品推荐
相关产品推荐

