Android中SQLite存图片URI后加载触发SecurityException的解决方法
嘿,这个问题我太熟悉了!你碰到的SecurityException本质是个权限生命周期的坑——从相册拿到的图片URI只有临时访问权限,存进SQLite之后再取出来用,之前的临时授权早就过期了,系统自然会阻止你访问资源。而且还有个隐形隐患:如果用户后续删除了相册里的这张图,你存在数据库里的URI直接就失效了。
下面给你两种靠谱的解决方案,优先推荐第一种:
解决方案1:复制图片到应用私有目录,存储私有路径
这是最稳妥的方案,把选中的图片复制到应用自己的私有存储区域,这样你完全掌控文件的生命周期,再也不会有权限问题。
步骤1:在onActivityResult里复制图片到私有目录
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // 替换成你自己的图片选择请求码 if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null) { Uri imageUri = data.getData(); if (imageUri != null) { try { // 打开相册图片的输入流 InputStream inputStream = getContentResolver().openInputStream(imageUri); // 在应用内部存储创建唯一命名的图片文件(避免重名) File outputFile = new File(getFilesDir(), "prod_img_" + System.currentTimeMillis() + ".png"); // 复制文件到私有目录 FileOutputStream outputStream = new FileOutputStream(outputFile); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } // 记得关闭流 inputStream.close(); outputStream.close(); // 把私有目录的文件路径存进SQLite String imagePath = outputFile.getAbsolutePath(); saveImagePathToDB(imagePath); // 替换成你的数据库存储方法 } catch (IOException e) { e.printStackTrace(); // 这里可以加个Toast提示用户图片保存失败 } } } }
步骤2:从数据库取路径加载图片
// 从数据库取出存储的路径 String savedImagePath = getImagePathFromDB(); // 替换成你的数据库查询方法 // 加载到ImageView File imageFile = new File(savedImagePath); if (imageFile.exists()) { // 推荐用Glide/Picasso这类图片库,自动处理缓存和内存 // Glide.with(this).load(imageFile).into(yourImageView); // 要是不想用第三方库,也可以用BitmapFactory Bitmap bitmap = BitmapFactory.decodeFile(savedImagePath); yourImageView.setImageBitmap(bitmap); }
解决方案2:申请持久化URI权限(仅Android 4.4+)
如果你不想复制文件占用额外存储,可以申请对相册图片URI的持久化访问权限,这样即使应用重启也能访问。但要注意:用户删除原图片后,URI还是会失效,而且只支持Android 19及以上版本。
步骤1:在onActivityResult中获取持久化权限
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null) { Uri imageUri = data.getData(); if (imageUri != null) { // 提取临时权限标识,转为持久化权限 int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); getContentResolver().takePersistableUriPermission(imageUri, takeFlags); // 把URI字符串存进SQLite saveImageUriToDB(imageUri.toString()); // 替换成你的数据库存储方法 } } }
步骤2:取出URI加载图片
// 从数据库取出URI字符串,解析为Uri对象 String savedUriStr = getImageUriFromDB(); // 替换成你的数据库查询方法 Uri savedUri = Uri.parse(savedUriStr); // 直接加载到ImageView yourImageView.setImageURI(savedUri); // 同样推荐用图片库:Glide.with(this).load(savedUri).into(yourImageView);
小提示
- 优先选方案1:完全独立控制图片,不受用户操作相册的影响,兼容性覆盖所有Android版本。
- 尽量用第三方图片加载库(比如Glide),它们会自动处理图片压缩、内存缓存、异常情况,比自己写BitmapFactory靠谱多了。
内容的提问来源于stack exchange,提问作者ShashankAC
相关产品推荐
相关产品推荐

