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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 04:33:20