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

Android Kotlin:选中图片永久保存至不可修改位置的实现问题

解决Android保存选中图库图片并永久加载的问题

原代码崩溃的核心原因是图库返回的Uri仅拥有临时访问权限,应用重启后权限失效,且用户可能删除原图,导致Uri无法正常加载。另外,drawable是编译时资源目录,运行时无法写入,所以不能用来保存用户选中的图片。

正确的方案是将图片复制到应用内部私有存储目录,这个目录只有应用本身能访问,用户无法修改或删除(除非卸载应用),适合永久保存。

1. 实现图片复制到内部存储的工具函数

private fun copyImageToInternalStorage(uri: Uri): String? {
    return try {
        // 从Uri获取输入流
        val inputStream = contentResolver.openInputStream(uri) ?: return null
        // 生成唯一文件名,避免重复
        val fileName = "user_selected_img_${System.currentTimeMillis()}.jpg"
        // 打开内部存储的输出流,MODE_PRIVATE确保只有本应用可访问
        val outputStream = openFileOutput(fileName, Context.MODE_PRIVATE)

        // 复制流
        inputStream.use { input ->
            outputStream.use { output ->
                input.copyTo(output)
            }
        }

        // 返回保存的绝对路径
        filesDir.absolutePath + File.separator + fileName
    } catch (e: IOException) {
        e.printStackTrace()
        null
    }
}

2. 修改图片选择后的处理逻辑(onActivityResult)

替换原来保存Uri的逻辑,改为复制图片并保存内部路径:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    val sharedPref = getSharedPreferences("facjasejf", 0)
    val editor = sharedPref.edit()
    val imageView = findViewById<ImageView>(R.id.facjata)

    if (resultCode == RESULT_OK && requestCode == 100) {
        val imageUri = data?.data ?: return

        // 复制图片到内部存储
        val internalPath = copyImageToInternalStorage(imageUri)
        internalPath?.let { path ->
            // 加载图片到ImageView
            imageView.setImageBitmap(BitmapFactory.decodeFile(path))
            // 保存路径到SharedPreferences
            editor.putString("saved_image_path", path)
            editor.apply() // apply异步提交,比commit更高效
        }
    }
}

3. 应用启动时加载保存的图片

在onCreate中读取保存的路径并加载图片:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val imageView = findViewById<ImageView>(R.id.facjata)
    val sharedPref = getSharedPreferences("facjasejf", 0)
    val savedPath = sharedPref.getString("saved_image_path", null)

    savedPath?.let { path ->
        // 处理大图片避免OOM,可使用采样解码
        val bitmap = decodeSampledBitmapFromFile(path, imageView.width, imageView.height)
        imageView.setImageBitmap(bitmap)
    }
}

// 可选:大图片采样解码工具函数,防止内存溢出
private fun decodeSampledBitmapFromFile(path: String, reqWidth: Int, reqHeight: Int): Bitmap {
    val options = BitmapFactory.Options()
    options.inJustDecodeBounds = true
    BitmapFactory.decodeFile(path, options)

    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
    options.inJustDecodeBounds = false

    return BitmapFactory.decodeFile(path, options)
}

private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
    val height = options.outHeight
    val width = options.outWidth
    var inSampleSize = 1

    if (height > reqHeight || width > reqWidth) {
        val halfHeight = height / 2
        val halfWidth = width / 2
        while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2
        }
    }
    return inSampleSize
}

关键说明

  • 内部存储优势:filesDir属于应用私有目录,Android系统会阻止其他应用和用户访问,保证图片不会被意外修改或删除。
  • 权限问题:Android 10及以上版本,通过ContentResolver读取图库图片无需READ_EXTERNAL_STORAGE权限(你已申请也不影响)。
  • 避免OOM:如果用户选中的图片尺寸较大,必须使用采样解码函数缩小图片,防止内存溢出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 05:44:53