如何使用存储在Room中的URI字符串加载图像

3

我正在制作一个练习应用程序,用于加载商店的库存。在屏幕内,我按下一个浮动按钮,生成一个对话框,询问用户从他们的图库中选择的多个数据中的图像。稍后,在对话框中按下保存按钮时,图像和其余数据将保存在ViewModel和ROOM中,然后在主屏幕上生成一个项目,同时显示这些数据,并使用Log.d打印。

在保存后生成项目时,它会正确显示,但是,如果我重新启动应用程序,则图像会消失。在生成图像和重新启动应用程序时,Log.d打印在两种情况下都显示相同的URI。

我的主要目标是在ROOM中保存图像、其地址或URI,然后加载带有图像的项目。我的研究表明,将URI保存为字符串是正确的做法,但我不知道保存图像的正确做法,如果必要,我也可以接受其他解决方案。

首先,在创建项目的对话框中,我像这样从图库中选择图像,并将其保存在ViewModel中,稍后保存到ROOM中:

val singlePhotoPickerLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickVisualMedia(),
onResult = { uri ->
selectedImageUri = uri
Log.d("URI RESULT", "$uri")
viewModel.onDialogChanged(
uri.toString()
    )
  }
)

当我按下“保存”按钮时,我将其保存在ROOM中:

DialogButton(
ButtonDefaults.buttonColors(
backgroundColor = verdeBillete,
contentColor = Color.White
), "Guardar", modifier = Modifier, onClick = {
         viewModel.viewModelScope.launch {
         viewModel.onAddSelected(
          inventoryItem(
            0,
            uri,
               )
         )
   }
onDismiss()
})

//add to ViewModel
fun onAddSelected(addItem: inventoryItem) {
viewModelScope.launch {
addItem(addItem)
getInventory()
}
}

//ROOM Table

@Entity(tableName = "inventory")
data class inventoryItem(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "r_id")
var id: Int = 0,
@ColumnInfo(name = "r_uri")
val uri: String,
)

然后我目前尝试像这样加载图像:

Log.d("Loading items", item.uri)
AsyncImage(
model = Uri.parse(item.uri),
contentDescription = null,
modifier = Modifier.fillMaxWidth(),
contentScale = ContentScale.Crop
)

从图库中选择图片后,该图片是可见的,但是在重新启动应用程序后,该图片会消失。在这两种情况下,Log.d 中打印的 URI 是相同的。

此外,我已经获得了以下权限:

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        tools:ignore="ScopedStorage" />

更新:在阅读了CommonsWare和Gowtham K K的两个答案并尝试实现它们后,我无法自己编写代码,因此我将帖子的内容(问题和两个答案)输入chatgpt并要求解决方案,它为我提供了以下解决方案,对我有用。

要使用takePersistableUriPermission,您必须执行以下操作:

首先,您需要具有读取或写入要持久保存的URI的权限。您可以通过在AndroidManifest.xml文件中添加以下代码行来实现:

然后,您需要获取要持久保存的URI。例如,如果您想保存从库中选择的图像的URI,则可以使用ActivityResultContracts.PickVisualMedia方法,如下所示:

    val singlePhotoPickerLauncher =
    rememberLauncherForActivityResult( contract =
    ActivityResultContracts.PickVisualMedia(), onResult = { uri ->
    selectedImageUri = uri } )

一旦你有了URI,你可以使用takePersistableUriPermission来持久保存它。takePersistableUriPermission方法应该在ContentResolver上使用,并带有两个参数:URI和访问模式(读或写)。例如:
contentResolver.takePersistableUriPermission(uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION) or
 
contentResolver.takePersistableUriPermission(uri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION)

最后,您可以将URI保存在ROOM数据库中作为文本字符串,并在需要时在应用程序中加载它。例如:
val inventoryItem = inventoryItem(0, uri.toString())
viewModel.onAddSelected(inventoryItem)

将所有东西组合在一起:

var selectedImageUri by remember {
        mutableStateOf<Uri?>(null)
    }

    val singlePhotoPickerLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.PickVisualMedia(),
        onResult = { uri ->
            selectedImageUri = uri
            Log.d("URI RESULT", "$uri")
            val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION//or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
            val resolver = mContext.contentResolver
            resolver.takePersistableUriPermission(uri!!, flags)
            viewModel.onDialogChanged( //**save to database function**
                uri.toString()
            )
        }
    )

1
https://commonsware.com/blog/2020/08/08/uri-access-lifetime-still-shorter-than-you-might-think.html - CommonsWare
1个回答

2
这是因为应用程序进程被杀死时,URI会被撤销。
对于存储访问框架的URI,您可以使用takePersistableUriPermission获得长期权限。
但据我所知,这可能不适用于ActivityResultContracts.PickVisualMedia()
在您的情况下,您可以在第一次获取图像后制作自己的副本,并将其保存在应用程序特定的存储中,并将该URI保存在您的数据库中。 这将更加灵活。即使原始文件被删除/您仍然可以访问已复制的图像。

我们如何获取应用程序专用存储中已保存图像的URI? - Hamed
如何获取应用程序特定存储中已保存图像的URI? - Hamed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接