简体   繁体   English

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

[英]How to load an image using a URI stored in Room as a String

I am making a practice application to load the inventory of a store, inside the screen I press a floating button that generates a dialog that asks for an image among several data, which the user selects from their gallery, later when pressing the save button in the dialog, the image and the rest of the data are saved in the ViewModel and ROOM, to then generate an item on the main screen that shows these data at the same time that they are printed with Log.d我正在制作一个练习应用程序来加载商店的库存,在屏幕内我按下一个浮动按钮,该按钮生成一个对话框,要求用户从他们的图库中选择多个数据中的图像,稍后在按下保存按钮时对话框、图像和数据的 rest 保存在 ViewModel 和 ROOM 中,然后在主屏幕上生成一个项目,在使用 Log.d 打印的同时显示这些数据

When generating the item after saving, it is shown correctly, however, if I restart the application the image disappears.保存后生成项目时,它会正确显示,但是,如果我重新启动应用程序,图像就会消失。 Both when generating the image and when restarting the application, the Log.d print shows the same URI in both cases.在生成图像和重新启动应用程序时,Log.d 打印在这两种情况下都显示相同的 URI。

My main goal is to save an image, its address, or URI in ROOM, and then load the item with the image.我的主要目标是在 ROOM 中保存图像、它的地址或 URI,然后用图像加载项目。 My research leads me to believe that it is correct to save the URI as a string, but I am unaware of the proper practices for saving an image and I am open to other ways to reach a solution if necessary.我的研究使我相信将 URI 保存为字符串是正确的,但我不知道保存图像的正确做法,并且我愿意在必要时使用其他方法来找到解决方案。

First, in a dialog to create an item, I select an image from the gallery like this and save it in the ViewModel and later in ROOM:首先,在创建项目的对话框中,我 select 像这样从画廊中获取图像并将其保存在 ViewModel 中,然后保存在 ROOM 中:

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

I save it in ROOM when I press the 'Save' button:当我按下“保存”按钮时,我将它保存在 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,
)

Then I currently try to load the image like this:然后我目前尝试像这样加载图像:

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

Just after selecting the image from the gallery, the image is visible, however, after restarting the application the image disappears.从图库中选择图像后,图像是可见的,但是,在重新启动应用程序后图像消失。 In both cases the printed URI in Log.d is the same.在这两种情况下,Log.d 中打印的 URI 是相同的。

Also, I have permission for:另外,我有权:

 <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" />

Update: After reading both answers from CommonsWare and Gowtham KK (Thank you both,) and trying to implement them, I couldn't write the code myself, so I entered the content of the post (the question and both answers) into chatgpt and asked for a solution.更新:在阅读了 CommonsWare 和 Gowtham KK(谢谢你们)的两个答案并尝试实现它们之后,我无法自己编写代码,所以我将帖子的内容(问题和两个答案)输入到 chatgpt 和寻求解决方案。 which presented me with the following solution which worked for me.这为我提供了以下对我有用的解决方案。

To use takePersistableUriPermission, you must do the following:要使用 takePersistableUriPermission,您必须执行以下操作:

First, you need to have permissions to read or write the URI that you want to save persistently.首先,您需要具有读取或写入要持久保存的 URI 的权限。 You can do this by adding the following line of code in your AndroidManifest.xml file:您可以通过在 AndroidManifest.xml 文件中添加以下代码行来执行此操作:

or或者

Then, you need to obtain the URI that you want to save persistently.然后,您需要获取要持久保存的 URI。 For example, if you want to save the URI of an image selected from the gallery, you can use the ActivityResultContracts.PickVisualMedia method as follows:例如,如果要保存从图库中选择的图像的 URI,可以使用 ActivityResultContracts.PickVisualMedia 方法,如下所示:

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

Once you have the URI, you can use takePersistableUriPermission to save it persistently.获得 URI 后,您可以使用 takePersistableUriPermission 持久保存它。 The takePersistableUriPermission method should be used on the ContentResolver and takes two parameters: the URI and the access mode (read or write). takePersistableUriPermission 方法应该用在 ContentResolver 上,它有两个参数:URI 和访问模式(读或写)。 For example:例如:

contentResolver.takePersistableUriPermission(uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION) or
 
contentResolver.takePersistableUriPermission(uri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION)

Finally, you can save the URI in your ROOM database as a text string and load it in your application when necessary.最后,您可以将 URI 作为文本字符串保存在您的 ROOM 数据库中,并在需要时将其加载到您的应用程序中。 For example:例如:

val inventoryItem = inventoryItem(0, uri.toString())
viewModel.onAddSelected(inventoryItem)

Putting everything together:把所有东西放在一起:

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()
            )
        }
    )

This is because the URI would get revoked when app process get killed.这是因为当应用程序进程被终止时,URI 将被撤销。

For Storage access framework URIs you can get long term permission using takePersistableUriPermission .对于存储访问框架 URI,您可以使用takePersistableUriPermission获得长期许可。

But It might not work for ActivityResultContracts.PickVisualMedia() as far as I know.但据我所知,它可能不适用于ActivityResultContracts.PickVisualMedia()

In your case you can make your own copy of the image after getting first time and save it in the app specific storage and save that URI in your db.在您的情况下,您可以在第一次获取图像后制作自己的图像副本,并将其保存在应用程序特定的存储中,并将该 URI 保存在您的数据库中。 This will be more flexible.这样会更灵活。 Even if original file gets deleted / you will still can get access of your copied image.即使原始文件被删除/您仍然可以访问您复制的图像。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM