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

安卓绘图应用编辑图库图片保存时私有存储空Bitmap、共享存储缺原图问题

问题定位&修复方案

问题1:私有存储保存空文件

你在API<28的分支中创建了私有目录的文件输出流,但从未将Bitmap数据写入该流,仅做了空流的flush和关闭,所以得到空文件。同时API 28+分支完全没有写入私有目录的逻辑,导致高版本系统私有存储也没有对应文件。

问题2:保存结果缺失原图仅保留绘制内容

你从图库加载并缩放得到的loadedBitmap默认是**不可变(Immutable)**类型,直接将其绑定到Canvas后,调用draw(canvas)产生的绘制内容无法写入到loadedBitmap中,导致最终保存的文件没有合并原图和绘制修改。


具体修复代码

第一步:修改Bitmap初始化逻辑,确保loadedBitmap是可变副本

if (bitmap != null) {
    // 缩放原图后转为可变Bitmap,才能写入绘制内容
    Bitmap scaled = scale(bitmap, width, height);
    // 创建可变副本,配置和原缩放图一致
    loadedBitmap = scaled.copy(Bitmap.Config.ARGB_8888, true);
    // 回收无用的原图和临时缩放图,避免内存泄漏
    bitmap.recycle();
    scaled.recycle();
} else {
    this.bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888, true);
}

第二步:修复保存方法逻辑

@SuppressLint("WrongThread")
public void saveImage() throws IOException {
    String filename = "appName" + System.currentTimeMillis();
    Bitmap targetBitmap;
    // 先合并原图和绘制内容到最终要保存的Bitmap
    if(loadedBitmap != null){
        // 用可变的loadedBitmap创建Canvas
        Canvas canvas = new Canvas(loadedBitmap);
        // 调用View的draw方法把用户绘制的内容画到loadedBitmap上,合并原图和修改
        draw(canvas);
        targetBitmap = loadedBitmap;
    } else{
        Canvas canvas = new Canvas(bitmap);
        draw(canvas);
        targetBitmap = bitmap;
    }

    // 先统一写入应用私有存储,解决空文件问题
    ContextWrapper cw = new ContextWrapper(getContext());
    File directory = cw.getDir("files", Context.MODE_PRIVATE);
    pathString = directory.getAbsolutePath();
    File privateFile = new File(directory, filename + ".png");
    FileOutputStream privateFos = new FileOutputStream(privateFile);
    // 把最终Bitmap写入私有流
    targetBitmap.compress(Bitmap.CompressFormat.PNG, 100, privateFos);
    privateFos.flush();
    privateFos.close();

    // 再处理公共存储保存逻辑
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        OutputStream fos;
        ContentResolver resolver = context.getContentResolver();
        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, filename + ".png");
        // 统一MIME类型和压缩格式,避免格式不匹配
        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
        contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
        
        Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
        fos = resolver.openOutputStream(Objects.requireNonNull(imageUri));
        // 直接用已经合并好的targetBitmap压缩
        targetBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        Objects.requireNonNull(fos).close();
        imageSaved = true;
    } else {
        if(ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
            // 直接把已经合并好的Bitmap插入MediaStore
            MediaStore.Images.Media.insertImage(context.getContentResolver(), targetBitmap, filename, "made with appName");
            addImageToGallery(privateFile.getPath(), context);
            imageSaved = true;
        }else{
            requestPermissions((Activity) context, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, 100);
        }
    }
}

第三步:优化图库选图逻辑(可选,避免分区存储下路径获取失败)

Android 10以上通过MediaStore.Images.Media.DATA获取路径的方式已经被废弃,建议直接将选中图片的Uri传递给绘图页,用ContentResolver.openInputStream加载Bitmap,避免部分机型拿不到路径导致加载失败:

// onActivityResult中直接传Uri而不是路径
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK && requestCode == IMAGE && data != null) {
        Uri selectedImageUri = data.getData();
        Intent intent = new Intent(getApplicationContext(), ArtActivity.class);
        intent.putExtra("imageUri", selectedImageUri.toString());
        startActivity(intent);
    }
}

绘图页接收Uri后用如下方式加载Bitmap:

Uri imageUri = Uri.parse(getIntent().getStringExtra("imageUri"));
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 01:27:00