API 31+下图片保存失败及相机调用异常问题求助
API 31+ 图片保存与相机问题解决方案
一、图片保存失败问题(含分享场景)
核心原因
API 31+ 收紧了外部存储权限管控:
Environment.getExternalStorageDirectory()属于共享存储区域,直接通过File类写入会触发权限异常,需改用MediaStoreAPI操作getExternalFilesDir(null)是应用私有外部存储,无需权限但文件仅当前应用可见,且原代码仅传入目录未指定具体文件名,导致写入逻辑失效
解决步骤
1. 保存到应用私有目录(仅应用内使用)
修正buttonSaveImageClicked的路径传递逻辑,并确保SaveLayoutToFile内部正确写入文件:
public void buttonSaveImageClicked(View view) throws IOException { editorImage.setDrawingCacheEnabled(true); Bitmap bitmap = editorImage.getDrawingCache(); SaveLayoutToFile saveImage = new SaveLayoutToFile(this, bitmap, editorImage); // 拼接完整文件名,而非仅目录 String filePath = new File(getExternalFilesDir(null), "spooky.png").getAbsolutePath(); saveImage.execute(filePath); } // 示例SaveLayoutToFile的核心写入逻辑 @Override protected Boolean doInBackground(String... params) { String fullPath = params[0]; try (FileOutputStream fos = new FileOutputStream(fullPath)) { bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); return true; } catch (IOException e) { e.printStackTrace(); return false; } }
2. 保存到共享存储(DCIM目录,支持分享)
改用MediaStore API写入共享存储,无需直接操作File:
public File saveFileToShare() { ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DISPLAY_NAME, "attachment.png"); values.put(MediaStore.Images.Media.MIME_TYPE, "image/png"); values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/Camera"); Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); if (uri == null) return null; try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) { editorImage.setDrawingCacheEnabled(true); Bitmap bitmap = editorImage.getDrawingCache(); boolean saveSuccess = bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); editorImage.setDrawingCacheEnabled(false); if (saveSuccess) { // 可选:获取实际文件路径(分享时直接用Uri更安全) String[] projection = {MediaStore.Images.Media.DATA}; try (Cursor cursor = getContentResolver().query(uri, projection, null, null, null)) { if (cursor != null && cursor.moveToFirst()) { String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)); return new File(path); } } } } catch (Exception e) { e.printStackTrace(); } return null; }
权限配置:在AndroidManifest.xml中添加适配权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" />
同时需在代码中动态申请对应权限(API 23+)。
二、相机无法打开问题
核心原因
- API 31+ 无法直接在
Environment.getExternalStorageDirectory()路径创建文件 - 缺少相机权限,且FileProvider配置可能存在错误
- 未授予相机应用写入临时文件的权限
解决步骤
1. 修正拍照逻辑,改用应用私有存储存临时文件
public void cameraClicked(View view) { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 先检查是否有可用相机应用 if (takePictureIntent.resolveActivity(getPackageManager()) == null) { return; } // 用应用私有外部存储存放临时照片 File tempDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); if (!tempDir.exists()) tempDir.mkdirs(); File tempFile = new File(tempDir, "photoTemp.png"); try { if (tempFile.exists()) tempFile.delete(); tempFile.createNewFile(); Uri uri = FileProvider.getUriForFile( this, getPackageName() + ".provider", tempFile); // 必须授予相机应用写入权限 takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(takePictureIntent, 2); } catch (IOException e) { e.printStackTrace(); } }
2. 检查FileProvider配置
在AndroidManifest.xml中确保已注册FileProvider:
<application> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application>
在res/xml目录下创建file_paths.xml:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="external_files" path="." /> </paths>
3. 添加相机权限
在AndroidManifest.xml中添加:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" android:required="false" />
并在代码中动态申请相机权限(API 23+)。
内容的提问来源于stack exchange,提问作者JustMeShortie
相关产品推荐
相关产品推荐

