我的目标是从相机应用程序请求图片,将其保存在我的应用数据中,并将相同的图片写入图库,以便如果用户删除应用程序,则仍具有拍摄的照片(与Messenger不同,我们的实用工具照片即使没有应用程序也被认为对用户非常有价值)。
我面临的问题与29个API级别紧密相关,这是构建目标(提供降级选项不可接受)。
所以基本上我遇到的问题是:
第一个问题:即使授予了“Manifest.permission.WRITE_EXTERNAL_STORAGE”权限,我也无法访问使用“Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)”构造的文件,我会收到IOException(权限被拒绝)。
第二个问题:预先Q设备(Nokia 8 Android Pie)会在contentResolver.insert(contentUri,contentValues)上抛出异常。
如果我在Android 10设备上运行(在我的情况下是模拟器),使用
主要问题是如何正确地将图片写入Android托管的图库文件夹,以便在面向29Api级别的情况下可以在Android 10之前的设备上运行?
额外参考:
我从我的应用程序缓存存储中将图片写入Android 10图库的代码是:
我面临的问题与29个API级别紧密相关,这是构建目标(提供降级选项不可接受)。
所以基本上我遇到的问题是:
第一个问题:即使授予了“Manifest.permission.WRITE_EXTERNAL_STORAGE”权限,我也无法访问使用“Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)”构造的文件,我会收到IOException(权限被拒绝)。
第二个问题:预先Q设备(Nokia 8 Android Pie)会在contentResolver.insert(contentUri,contentValues)上抛出异常。
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(..)
选项可以正常工作。主要问题是如何正确地将图片写入Android托管的图库文件夹,以便在面向29Api级别的情况下可以在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>