Android本地图片与持久化数据关联的实现方案咨询
解决Android中Room对象与本地图片关联的问题
我来帮你理清这个困扰,咱们一步步解决:
为什么存储的Uri重启后失效?
你遇到的核心问题是临时权限:通过ACTION_OPEN_DOCUMENT获取的Uri,系统只授予了临时访问权限,重启后这个权限就会被回收,导致Uri无法再访问。所以直接存Uri到数据库是不行的,得用下面两种方案之一。
方案一:申请持久化Uri权限(保留原图片位置)
首先纠正你代码的编译错误:takePersistableUriPermission()的返回值是void,它的作用是把临时权限转为持久化权限,而不是获取ParcelFileDescriptor。正确的步骤是:
- 在获取到Uri后(比如
onActivityResult里)先申请持久化权限:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == READ_REQUEST_CODE && resultCode == RESULT_OK) { Uri uri = data.getData(); // 申请持久化权限,这一步必须做,不然重启后权限失效 getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // 现在把这个uri存到Room数据库里 yourDao.insert(new YourEntity(..., uri.toString())); } }
- 加载图片的正确代码:
private Bitmap getBitmapFromUri(Uri uri) throws IOException { // 用openFileDescriptor获取文件描述符,而不是takePersistableUriPermission ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r"); FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); parcelFileDescriptor.close(); return image; }
注意:只有通过ACTION_OPEN_DOCUMENT/ACTION_OPEN_DOCUMENT_TREE获取的Uri才能申请持久化权限,普通文件Uri不行。
方案二:复制图片到App私有目录(更简单稳定)
如果不需要保留用户原图片的位置,这个方案更省心——把选中的图片复制到App的私有存储目录,然后在Room里存储文件路径。私有目录的文件只有你的App能访问,完全不用处理权限失效的问题。
1. 处理系统文件管理器选择的图片
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == READ_REQUEST_CODE && resultCode == RESULT_OK) { Uri uri = data.getData(); try { // 创建私有目录下的目标文件 File destDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); if (!destDir.exists()) destDir.mkdirs(); File destFile = new File(destDir, "img_" + System.currentTimeMillis() + ".jpg"); // 复制文件 InputStream inputStream = getContentResolver().openInputStream(uri); OutputStream outputStream = new FileOutputStream(destFile); byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); } inputStream.close(); outputStream.close(); // 存储文件路径到Room yourDao.insert(new YourEntity(..., destFile.getAbsolutePath())); } catch (IOException e) { e.printStackTrace(); } } }
2. 处理相机拍照的图片
直接让相机把照片保存到私有目录,然后存储路径:
private static final int CAMERA_REQUEST_CODE = 1002; private void takePhoto() { // 创建私有目录下的照片文件 File photoDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); if (!photoDir.exists()) photoDir.mkdirs(); File photoFile = new File(photoDir, "camera_img_" + System.currentTimeMillis() + ".jpg"); // 使用FileProvider获取Uri(Android 7.0+要求) Uri photoUri = FileProvider.getUriForFile(this, "com.your.package.fileprovider", photoFile); // 启动相机Intent Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); startActivityForResult(takePictureIntent, CAMERA_REQUEST_CODE); } // 拍照完成后,直接存储路径 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == CAMERA_REQUEST_CODE && resultCode == RESULT_OK) { // 这里的photoFile就是之前创建的文件,直接取路径 yourDao.insert(new YourEntity(..., photoFile.getAbsolutePath())); } }
方案选择建议
- 如果你需要保留用户原图片的位置(比如用户删除App后图片还在),选方案一;
- 如果你追求简单、稳定,不想处理权限问题,选方案二——这个更适合你的需求(无需考虑用户删除图片)。
内容的提问来源于stack exchange,提问作者ro.nin
相关产品推荐
相关产品推荐

