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
相关产品推荐
相关产品推荐

