MediaStore contentResolver.insert() 在拍照时创建副本而不是替换现有文件(Android Q:29)

Ang*_*Koh 5 android android-10.0

我正在尝试使用以下代码保存从相机拍摄的图像:

@RequiresApi(Build.VERSION_CODES.Q)
private fun setImageUri(): Uri {
    val resolver = contentResolver
    val contentValues = ContentValues().apply {
        put(MediaStore.MediaColumns.DISPLAY_NAME, "house2.jpg")
        put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
        put(MediaStore.MediaColumns.RELATIVE_PATH, "Pictures/OLArt")
    }

    imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)

    return imageUri!!
}
Run Code Online (Sandbox Code Playgroud)

该功能第一次运行良好。然而,当图像(house2.jpg)已经存在时,系统将创建另一个名为“house2 (1).jpg”、“house2 (2).jpg”等的文件(而不是替换旧文件)

在此处输入图片说明

我可以在 contentValues 中设置什么来强制解析器替换文件而不是创建它的副本吗?

下面是拍照意图的代码。

 Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->

     takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, setImageUri()) //<- i paste in the imageUri here

     // Ensure that there's a camera activity to handle the intent
     takePictureIntent.resolveActivity(packageManager)?.also {

         startActivityForResult(takePictureIntent, 102)
     }
  }
Run Code Online (Sandbox Code Playgroud)

Ang*_*Koh 5

@CommonsWare 的评论有帮助。

这个想法是

  1. 使用resolver.query()查询文件是否已存在
  2. 如果是,则从光标中提取contentUri
  3. 否则,使用resolver.insert()

创建查询选择时需要注意的一件事是 MediaStore.MediaColumns.RELATIVE_PATH 需要终止“/”

即 'Pictures/OLArt/' << 注意 OLArt/ 后面的斜杠

    val selection = "${MediaStore.MediaColumns.RELATIVE_PATH}='Pictures/OLArt/' AND " 
                   + "${MediaStore.MediaColumns.DISPLAY_NAME}='house2.jpg' "
Run Code Online (Sandbox Code Playgroud)

以下是更新后的代码。

@RequiresApi(Build.VERSION_CODES.Q)
private fun getExistingImageUriOrNullQ(): Uri? {
    val projection = arrayOf(
        MediaStore.MediaColumns._ID,
        MediaStore.MediaColumns.DISPLAY_NAME,   // unused (for verification use only)
        MediaStore.MediaColumns.RELATIVE_PATH,  // unused (for verification use only)
        MediaStore.MediaColumns.DATE_MODIFIED   //used to set signature for Glide
    )

    // take note of the / after OLArt
    val selection = "${MediaStore.MediaColumns.RELATIVE_PATH}='Pictures/OLArt/' AND " 
                  + "${MediaStore.MediaColumns.DISPLAY_NAME}='house2.jpg' "

    contentResolver.query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        projection, selection, null, null ).use { c ->
        if (c != null && c.count >= 1) {

            print("has cursor result")
            c.moveToFirst().let {

                val id = c.getLong(c.getColumnIndexOrThrow(MediaStore.MediaColumns._ID) )
                val displayName = c.getString(c.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME) )
                val relativePath = c.getString(c.getColumnIndexOrThrow(MediaStore.MediaColumns.RELATIVE_PATH) )
                lastModifiedDate = c.getLong(c.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED) )

                imageUri = ContentUris.withAppendedId(   
                             MediaStore.Images.Media.EXTERNAL_CONTENT_URI,  id)

                print("image uri update $displayName $relativePath $imageUri ($lastModifiedDate)")

                return imageUri
            }
        }
    }
    print("image not created yet")
    return null
}
Run Code Online (Sandbox Code Playgroud)

然后我将此方法添加到我现有的代码中

@RequiresApi(Build.VERSION_CODES.Q)
private fun setImageUriQ(): Uri {

    val resolver = contentResolver

    imageUri = getExistingImageUriOrNullQ() //try to retrieve existing uri (if any)
    if (imageUri == null) {

       //=========================
       // existing codes for resolver.insert
       //(SNIPPED)
       //=========================
    }
    return imageUri!!
}
Run Code Online (Sandbox Code Playgroud)