使用 Android 上的 Glide 以 "content://" 方案显示来自 URI 的图像 Q
Display image from URI with "content://" scheme with Glide on Android Q
关于AndroidQ和Glide的问题以前有人问过,但是我不能把它们都放在一起,所以我希望有人愿意提供帮助。
我正在将从云 (Firebase) 下载或从图库中选择或用相机拍摄的图像保存到本地存储。我曾经将它保存在应用程序的文件夹中,并且可以将它添加到图库中,但是使用 Android Q 这不再可能了。所以我更改了我的应用程序以使用 MediaStore 和 ContentResolver 来访问文件并保存它。文件保存在MediaStore.Images.Media.EXTERNAL_CONTENT_URI
.
的Pictures
目录下
存储处理器:
public Uri createUriForFile(String fileName, String parentId)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
Uri uriFromContentResolver = getUriFromContentResolver(fileName);
if(uriFromContentResolver != null)
{
return uriFromContentResolver;
}else
{
ContentResolver resolver = baseActivity.getApplicationContext().getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");
contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, EXTERNAL_PICTURE_RELATIVE_PATH);
return resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
}
}else {
File pictureFile = new File( EXTERNAL_PICTURE_STORAGE_DIR.getAbsolutePath() + File.separator + parentId + File.separator + fileName );
checkAndCreatePictureDir(parentId);
return FileProvider.getUriForFile(baseActivity, baseActivity.getApplicationContext().getPackageName() + ".fileprovider", pictureFile);
}
}
public Uri getUriFromContentResolver(String fileName)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
ContentResolver resolver = baseActivity.getApplicationContext().getContentResolver();
Cursor queryCursor = resolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.RELATIVE_PATH}, MediaStore.Images.Media.DISPLAY_NAME + "=? ",
new String[]{fileName}, null);
if (queryCursor != null && queryCursor.moveToFirst())
{
Uri content = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon().appendEncodedPath(queryCursor.getString(queryCursor.getColumnIndex(MediaStore.Images.Media.RELATIVE_PATH)))
.appendPath(queryCursor.getString(queryCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME))).build();
return content;
} else
{
return null;
}
}else
{
return null;
}
它将文件保存到存储中,可以在 Android Studio 中使用设备资源管理器查看。它也被上传到云端并可以从那里查看(如果它是一个新文件)。但是当我尝试使用 Glide 将它加载到 ImageView
中时,它给了我一个错误:java.lang.UnsupportedOperationException: Unknown or unsupported URL: content://
。我似乎无法弄清楚为什么。它曾经工作并且 URI 应该是正确的,因为它也用于其他地方(上传文件,保存文件等)并且它工作。 Glide/my 代码有什么问题?
完整错误:
E/GlideExecutor: Request threw uncaught throwable
java.lang.UnsupportedOperationException: Unknown or unsupported URL: content://media/external/images/media/Pictures/**AppName**/IMG20200112164646.jpg
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:151)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:705)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1687)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1503)
at android.content.ContentResolver.openInputStream(ContentResolver.java:1187)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResourceFromUri(StreamLocalUriFetcher.java:85)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:60)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:15)
at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:62)
at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:302)
at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:272)
at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:233)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory.run(GlideExecutor.java:446)
你的getUriFromContentResolver()
错了。查询为 _ID
,然后使用 ContentUris.withAppendedId()
到 assemble 使用 Uri
。
例如,在this sample app from this book中,我使用:
suspend fun getLocalUri(filename: String): Uri? =
withContext(Dispatchers.IO) {
val resolver = context.contentResolver
resolver.query(collection, PROJECTION, QUERY, arrayOf(filename), null)
?.use { cursor ->
if (cursor.count > 0) {
cursor.moveToFirst()
return@withContext ContentUris.withAppendedId(
collection,
cursor.getLong(0)
)
}
}
null
}
其中 PROJECTION
和 QUERY
是:
private val PROJECTION = arrayOf(MediaStore.Video.Media._ID)
private const val QUERY = MediaStore.Video.Media.DISPLAY_NAME + " = ?"
collection
是:
private val collection =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) MediaStore.Video.Media.getContentUri(
MediaStore.VOLUME_EXTERNAL
) else MediaStore.Video.Media.EXTERNAL_CONTENT_URI
(在我的例子中,我正在查询视频;您可以改用 MediaStore.Images
)
关于AndroidQ和Glide的问题以前有人问过,但是我不能把它们都放在一起,所以我希望有人愿意提供帮助。
我正在将从云 (Firebase) 下载或从图库中选择或用相机拍摄的图像保存到本地存储。我曾经将它保存在应用程序的文件夹中,并且可以将它添加到图库中,但是使用 Android Q 这不再可能了。所以我更改了我的应用程序以使用 MediaStore 和 ContentResolver 来访问文件并保存它。文件保存在MediaStore.Images.Media.EXTERNAL_CONTENT_URI
.
Pictures
目录下
存储处理器:
public Uri createUriForFile(String fileName, String parentId)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
Uri uriFromContentResolver = getUriFromContentResolver(fileName);
if(uriFromContentResolver != null)
{
return uriFromContentResolver;
}else
{
ContentResolver resolver = baseActivity.getApplicationContext().getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");
contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, EXTERNAL_PICTURE_RELATIVE_PATH);
return resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
}
}else {
File pictureFile = new File( EXTERNAL_PICTURE_STORAGE_DIR.getAbsolutePath() + File.separator + parentId + File.separator + fileName );
checkAndCreatePictureDir(parentId);
return FileProvider.getUriForFile(baseActivity, baseActivity.getApplicationContext().getPackageName() + ".fileprovider", pictureFile);
}
}
public Uri getUriFromContentResolver(String fileName)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
ContentResolver resolver = baseActivity.getApplicationContext().getContentResolver();
Cursor queryCursor = resolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.RELATIVE_PATH}, MediaStore.Images.Media.DISPLAY_NAME + "=? ",
new String[]{fileName}, null);
if (queryCursor != null && queryCursor.moveToFirst())
{
Uri content = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon().appendEncodedPath(queryCursor.getString(queryCursor.getColumnIndex(MediaStore.Images.Media.RELATIVE_PATH)))
.appendPath(queryCursor.getString(queryCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME))).build();
return content;
} else
{
return null;
}
}else
{
return null;
}
它将文件保存到存储中,可以在 Android Studio 中使用设备资源管理器查看。它也被上传到云端并可以从那里查看(如果它是一个新文件)。但是当我尝试使用 Glide 将它加载到 ImageView
中时,它给了我一个错误:java.lang.UnsupportedOperationException: Unknown or unsupported URL: content://
。我似乎无法弄清楚为什么。它曾经工作并且 URI 应该是正确的,因为它也用于其他地方(上传文件,保存文件等)并且它工作。 Glide/my 代码有什么问题?
完整错误:
E/GlideExecutor: Request threw uncaught throwable
java.lang.UnsupportedOperationException: Unknown or unsupported URL: content://media/external/images/media/Pictures/**AppName**/IMG20200112164646.jpg
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:151)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:705)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1687)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1503)
at android.content.ContentResolver.openInputStream(ContentResolver.java:1187)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResourceFromUri(StreamLocalUriFetcher.java:85)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:60)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:15)
at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:62)
at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:302)
at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:272)
at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:233)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory.run(GlideExecutor.java:446)
你的getUriFromContentResolver()
错了。查询为 _ID
,然后使用 ContentUris.withAppendedId()
到 assemble 使用 Uri
。
例如,在this sample app from this book中,我使用:
suspend fun getLocalUri(filename: String): Uri? =
withContext(Dispatchers.IO) {
val resolver = context.contentResolver
resolver.query(collection, PROJECTION, QUERY, arrayOf(filename), null)
?.use { cursor ->
if (cursor.count > 0) {
cursor.moveToFirst()
return@withContext ContentUris.withAppendedId(
collection,
cursor.getLong(0)
)
}
}
null
}
其中 PROJECTION
和 QUERY
是:
private val PROJECTION = arrayOf(MediaStore.Video.Media._ID)
private const val QUERY = MediaStore.Video.Media.DISPLAY_NAME + " = ?"
collection
是:
private val collection =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) MediaStore.Video.Media.getContentUri(
MediaStore.VOLUME_EXTERNAL
) else MediaStore.Video.Media.EXTERNAL_CONTENT_URI
(在我的例子中,我正在查询视频;您可以改用 MediaStore.Images
)