如何使用 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_IDPHOTO_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 加载

如何在我的应用中使用 Glide 加载图像?

Android关于Glide的使用(高斯模糊加载监听圆角图片)

Android关于Glide的使用(高斯模糊加载监听圆角图片)

在Glide for Android中离线时加载已获取的图像

如何使用 glide 从 android 中的音频 url 获取缩略图