如何使用 Glide Android 加载带有“content://”前缀的 URI?
Posted
技术标签:
【中文标题】如何使用 Glide Android 加载带有“content://”前缀的 URI?【英文标题】:How to load a URI with "content://" prefix using Glide Android? 【发布时间】:2015-06-08 22:16:23 【问题描述】:我正在尝试使用 Glide 加载 URI 为“content://com.android.contacts/contacts/295”的联系人照片。
当我使用时
Glide.with(context).load(Uri.parse(contactPhoto).into(imageview)
Glide 给了我一个 FileNotFoundException
java.io.FileNotFoundException: File does not exist; URI: content://com.android.contacts/contacts/264, calling user: android.uid.shared:10006, calling package is one of: [com.android.providers.contacts, com.android.contacts, com.android.providers.userdictionary]
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:689)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1080)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:921)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:848)
at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:21)
at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:14)
at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:83)
at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:53)
at com.bumptech.glide.load.engine.DecodeJob.decodeSource(DecodeJob.java:170)
at com.bumptech.glide.load.engine.DecodeJob.decodeFromSource(DecodeJob.java:128)
at com.bumptech.glide.load.engine.EngineRunnable.decodeFromSource(EngineRunnable.java:122)
at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:101)
at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.java:58)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:52)
显然 Glide 试图从错误的位置获取图像。
如果有人指出如何使用“content://”URI 加载照片,我将不胜感激。
【问题讨论】:
github.com/bumptech/glide/issues/394 【参考方案1】:您需要创建一个使用 ContentResolver 的自定义加载器。 例如,在 Picasso 中,这是可行的,因为 already a custom request handler 使用了 ContentResolver。
我创建了一个自定义Glide loader for contacts for my internal use,您可以作为参考。
【讨论】:
感谢您的回答,我已经向 Glide 提交了一个原生实现此功能的拉取请求,我认为它很快就会被合并。 github.com/bumptech/glide/pull/1119 @AhmedI.Khalil 你能举个例子吗? github.com/bumptech/glide/issues/394 功能请求现已解决,它将在 Glide 中与 v3.8.0 一起发布。您要么必须等待 v3.8.0,要么可以将 Glide 源代码(分支 v3.0)作为依赖项包含在您的项目中,并查看我编写的这个示例。 github.com/bumptech/glide/blob/3.0/samples/contacturi/src/main/… @AhmedI.Khalil,是的,我看过示例,但它不起作用,因为我不知道它只会包含在 v3.8.0 中:(谢谢,伙计! 【参考方案2】:似乎 Glide 不会自动处理内容照片 Uri。
所以我已经使用 RxJava 方法解决了我的问题。
这是一个发出位图的方法(请注意调度程序,因为不滞后滚动性能很重要)
private Observable<Bitmap> _getConvertInputStreamToBitmapObservable(ContentResolver cr,
Uri contactUri)
return Observable.create(new Observable.OnSubscribe<Bitmap>()
@Override
public void call(Subscriber<? super Bitmap> subscriber)
InputStream inputStream =
ContactsContract.Contacts.openContactPhotoInputStream(cr, contactUri);
if (inputStream != null)
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
subscriber.onNext(bitmap);
subscriber.onCompleted();
).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
这是使用该方法的客户端代码(请注意取消订阅,因为它在回收中很重要)。
if (holder.bitmapSubscription != null)
holder.bitmapSubscription.unsubscribe();
holder.bitmapSubscription = _getConvertInputStreamToBitmapObservable(context.getContentResolver(),
contactUri)
.subscribe(holder.userImg::setImageBitmap);
【讨论】:
【参考方案3】:您需要为此使用ContentResolver
。
ContentResolver contextResolver = context.getContentResolver();
Uri uri = Uri.parse("content://com.android.contacts/contacts/295");
Bitmap thumbnail = null;
Cursor cursor = contentResolver.query(uri, new String[] ContactsContract.CommonDataKinds.Photo.PHOTO, null, null, null);
try
if (cursor.moveToFirst())
final byte[] thumbnailBytes = cursor.getBlob(0);
if (thumbnailBytes != null)
thumbnail = BitmapFactory.decodeByteArray(thumbnailBytes, 0, thumbnailBytes.length);
finally
cursor.close();
if (thumbnail != null)
imageView.setImageBitmap(thumbnail);
试试这个。这应该可以。
【讨论】:
是的,我知道有一个名为 openContactPhotoInputStream 的方法。但这会在主线程中检索照片,我在适配器中调用 Glide 代码并希望它处理在后台线程中获取图像。 我没有使用该方法,因为它在 API 14 下不起作用。而且我在这里看不到问题; Glide 不能比原生框架类更快。此外,Glide 不保证自动处理任何Uri
。
我得到了与public static final String PHOTO = DATA15
相关的java.lang.IllegalArgumentException: Invalid column data15
。使用 API 21。【参考方案4】:
Uri uri =
ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, new
Long(contactsPojo.getId()));
final Uri displayPhotoUri = Uri.withAppendedPath(uri,
ContactsContract.Contacts.Photo.DISPLAY_PHOTO);
Glide.with(context)
.load(displayPhotoUri)
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher)
.fallback(R.mipmap.ic_launcher)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(holder.image);
【讨论】:
【参考方案5】:我正在使用此代码然后成功运行
Glide.with(context)
.load(uri)
.addListener(new RequestListener<Drawable>()
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource)
Log.i(TAG, "onLoadFailed: ");
return false;
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource)
Log.i(TAG, "onResourceReady: ");
return false;
)
.into(holder.imgProfile);
【讨论】:
【参考方案6】:最快最简单的方法是先找出CONTACT_ID
。然后将这个CONTACT_ID
与PHOTO_URI
匹配。
//first create a cursor
val photoCursor = contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
photoProjection,
ContactsContract.Contacts._ID + "=?",
arrayOf(contactId),
null
)
//where photoProjection is like so
val photoProjection: Array<String> = arrayOf(
ContactsContract.Contacts.Photo.PHOTO_URI,
ContactsContract.Contacts._ID
)
//now grab the PHOTO_URI, this will only exist if there is a photo
val photo = if (photoCursor?.moveToFirst() == true)
photoCursor.getString(photoCursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI))
else
null
//In Glide now you can load the URI directly
Glide.with(this)
.load(uri)
.apply(imageTransform)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.into(image)
【讨论】:
以上是关于如何使用 Glide Android 加载带有“content://”前缀的 URI?的主要内容,如果未能解决你的问题,请参考以下文章
如何从用户手机获取联系人 URI 并在 android 中使用 glide 加载
Android关于Glide的使用(高斯模糊加载监听圆角图片)
Android关于Glide的使用(高斯模糊加载监听圆角图片)