Android API 13及以上版本存储权限申请指南(Java保存ImageView图片)
Android API 13+ 存储权限授予与图片保存(Java实现)
一、清单文件权限配置
根据不同API等级,在AndroidManifest.xml中声明对应权限:
API 13 - API 22
仅需声明外部存储写入权限,系统会在应用安装时自动授予:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
API 23 - API 28
除声明写入权限外,还需动态申请该危险权限(读取权限为可选,按需添加):
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
API 29+(Android 10及以上)
因Scoped Storage机制,保存图片到公共媒体目录无需声明WRITE_EXTERNAL_STORAGE;若需读取其他应用的媒体文件,API33+需添加:
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" />
若仅保存到应用私有外部存储(如getExternalFilesDir(Environment.DIRECTORY_PICTURES)),无需任何权限。
二、动态权限申请实现(API23+)
1. 权限检查与申请逻辑
在Activity/Fragment中实现权限校验与申请:
private static final int REQUEST_STORAGE_PERMISSION = 100; // 检查权限是否已授予 private boolean hasStoragePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // API33+:仅读取媒体文件需校验此权限,保存无需 return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED; } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // API23-32:校验写入外部存储权限 return ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; } // API13-22:权限已自动授予 return true; } // 发起权限申请 private void requestStoragePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_MEDIA_IMAGES}, REQUEST_STORAGE_PERMISSION); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERMISSION); } } // 处理权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_STORAGE_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限通过,执行保存操作 saveImageViewToStorage(); } else { Toast.makeText(this, "需要存储权限以保存图片", Toast.LENGTH_SHORT).show(); } } }
三、ImageView图片保存核心逻辑
结合权限校验,执行图片保存操作:
private void saveImageViewToStorage() { ImageView imageView = findViewById(R.id.your_image_view); Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap(); if (bitmap == null) { Toast.makeText(this, "图片为空,无法保存", Toast.LENGTH_SHORT).show(); return; } OutputStream outputStream = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // API29+:通过MediaStore保存到公共图片库 ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DISPLAY_NAME, "saved_img_" + System.currentTimeMillis() + ".png"); values.put(MediaStore.Images.Media.MIME_TYPE, "image/png"); values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/YourApp"); Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); if (uri != null) { try { outputStream = getContentResolver().openOutputStream(uri); bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); Toast.makeText(this, "图片已保存到图片库", Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show(); } finally { closeStream(outputStream); } } } else { // API13-28:保存到公共图片目录 File picturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); File appDir = new File(picturesDir, "YourApp"); if (!appDir.exists()) appDir.mkdirs(); File saveFile = new File(appDir, "saved_img_" + System.currentTimeMillis() + ".png"); try { outputStream = new FileOutputStream(saveFile); bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); Toast.makeText(this, "图片已保存至:" + saveFile.getAbsolutePath(), Toast.LENGTH_SHORT).show(); // 通知媒体库扫描新文件 MediaScannerConnection.scanFile(this, new String[]{saveFile.getAbsolutePath()}, null, null); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show(); } finally { closeStream(outputStream); } } } // 工具方法:关闭输出流 private void closeStream(OutputStream stream) { try { if (stream != null) stream.close(); } catch (IOException e) { e.printStackTrace(); } }
四、调用示例
在保存按钮的点击事件中触发流程:
findViewById(R.id.btn_save).setOnClickListener(v -> { if (hasStoragePermission()) { saveImageViewToStorage(); } else { requestStoragePermission(); } });
内容的提问来源于stack exchange,提问作者Muhammad Zeeshan Ali
相关产品推荐
相关产品推荐

