【发布时间】:2020-04-19 22:11:09
【问题描述】:
我的目标是从相机应用程序请求图片,将其保存在我的应用程序数据中,并将相同的图片写入图库,因此如果用户删除应用程序,它仍然有拍摄的照片(与信使不同,我们的实用工具图片被认为对用户即使没有应用程序)。
我面临的问题与 29 API 级别的女巫紧密相关是构建目标(不接受降级)。
所以基本上我得到的是:
第一个问题:即使授予Manifest.permission.WRITE_EXTERNAL_STORAGE,我也无法访问使用Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) 构造的文件,我得到IOException(权限被拒绝)
第二个问题:contentResolver.insert(contentUri, contentValues) 在 pre Q 设备(诺基亚 8 Android Pie)上抛出这个问题:
java.lang.SecurityException: Permission Denial: writing com.android.providers.media.MediaProvider uri content://media/external/images/media from .... requires android.permission.WRITE_EXTERNAL_STORAGE, or grantUriPermission()
如果我在 Android 10 设备(在我的情况下是模拟器)上运行,则使用 contentResolver.insert(..) 的选项可以正常工作。
主要问题当目标是 29Api 级别时,如何将图片正确写入 android 管理的画廊文件夹,以便它可以在 android 10 之前的设备上工作?
额外参考
我将图片从我的应用缓存存储写入 android 10 库的代码是:
fun writePictureToGalleryQ(context: Context, pictureUri: Uri, pictureName: String) {
val contentResolver = context.contentResolver
val relativeLocation = "${Environment.DIRECTORY_PICTURES}${File.separator}WaiJuDuDisAndroid"
val contentValues = ContentValues().apply {
put(MediaStore.Images.ImageColumns.DISPLAY_NAME, pictureName)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.ImageColumns.RELATIVE_PATH, relativeLocation)
}
val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
var uri: Uri? = null
uri = contentResolver.insert(contentUri, contentValues)
uri?.let { galleryPartUri ->
val outStream: OutputStream = contentResolver.openOutputStream(galleryPartUri)!!
val inStream: InputStream = contentResolver.openInputStream(pictureUri)!!
outStream.use {out ->
inStream.use { inpt ->
inpt.copyTo(out)
}
}
}
}
附:内容提供者在清单以及所需的权限和文件路径 xml 中设置。
应该为每个 Q android 版本执行的代码,但它看起来不像这样(假设我已经得到 Manifest.permission.WRITE_EXTERNAL_STORAGE)
fun writePictureToGalleryLegacy(context: Context, pictureUri: Uri, pictureName: String) {
val directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
val correctDir = File("${directory.absolutePath}${File.separator}WaiJuDuDisAndroid")
correctDir.mkdirs()
val file = File("${correctDir.absolutePath}${File.separator}${pictureUri.lastPathSegment}")
val contentResolver = context.contentResolver
val outStream: OutputStream = file.outputStream()
val inStream: InputStream = contentResolver.openInputStream(pictureUri)!!
outStream.use {out ->
inStream.use { inpt ->
inpt.copyTo(out)
}
}
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(file.extension)
MediaScannerConnection.scanFile(
context,
arrayOf(file.absolutePath),
arrayOf(mimeType?:"image/*"),
null
)
}
问题更新和编辑
我调整了我的代码块,使其更加正确,因为它们确实有效。
重建项目最终解决了我的问题。不知道我更讨厌什么——谷歌还是我自己。
当然,重建项目只是解决方案的一部分。
我遇到了两个问题:
- 这里有些人删除了他的答案,说我可能缺少
<application... android:requestLegacyExternalStorage="true" ....> - 我的依赖项中的某些库的清单中有
android:maxSdkVersion,这基本上会导致忽略我对 Android 10 和 Android 9 设备以及可能从 Android 4.4 开始的所有设备的权限。
因此我更改了我的应用清单:
<manifest>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/>
<application android:requestLegacyExternalStorage="true" ...>
...
</application>
</manifest>
【问题讨论】:
-
对于其余部分,不清楚您是否对 Q 或之前的 Q 有问题。
-
有了 Q 我没有问题。但是,当我尝试将相同的构建安装到 P 时。即使使用
android:requestLegacyExternalStorage="true",当使用Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)构建文件路径时,我也会得到open failed: EACCES (Permission denied) -
您可以在获得正确权限的情况下使用 Q 下面的路径。如果您想使用 MediaStore 插入文件或想要不使用,我们不清楚 Q 以下。为什么?
-
Bellow Q 我正在尝试将图片直接写入文件“/storage/emulated/0/Pictures/PIC_1577795479840.jpeg”,但我收到带有评论“权限被拒绝”的异常。我想做什么 - 我想以任何可能的方式将图片保存到 android 图片目录或 DCIM。
标签: android media android-permissions android-gallery