Как загрузить URI с префиксом content:// с помощью Glide Android?

Я пытаюсь загрузить фотографию контакта с URI «content://com.android.contacts/contacts/295» с помощью Glide.

Когда я использую

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 пытается получить изображение не с того места.

Я был бы признателен, если бы кто-нибудь указал мне, как загрузить фотографию с URI «content://».


person Ahmed I. Khalil    schedule 03.04.2015    source источник
comment
github.com/bumptech/glide/issues/394   -  person TWiStErRob    schedule 15.10.2015


Ответы (5)


Вам нужно создать собственный загрузчик, использующий ContentResolver. В Picasso, например, это работает, потому что есть уже является настраиваемым обработчиком запросов, который использует ContentResolver.

Я создал один собственный Glide-загрузчик контактов для внутреннего использования, который вы можете взять за основу.

person lujop    schedule 12.04.2016
comment
Спасибо за ваш ответ, я уже отправил запрос на включение в Glide, который изначально реализует эту функцию, я думаю, что он скоро будет объединен. github.com/bumptech/glide/pull/1119 - person Ahmed I. Khalil; 13.04.2016
comment
@AhmedI.Khalil Не могли бы вы опубликовать пример? - person Mussa; 13.05.2016
comment
github.com/bumptech/glide/issues/394 Запрос функции теперь решен и он будет выпущен с v3.8.0 в Glide. Вам либо придется дождаться версии 3.8.0, либо вы можете включить исходный код Glide (ветвь версии 3.0) в качестве зависимости в свой проект и взглянуть на этот пример, который я написал. github.com/bumptech/glide/blob/3.0/samples/contacturi/src/main/ - person Ahmed I. Khalil; 13.05.2016
comment
@AhmedI.Khalil, Да, я видел образец, и он не сработал, потому что я не знал, что он будет включен только в v3.8.0 :( Спасибо, чувак! - person Mussa; 13.05.2016

Похоже, что 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);
person Ahmed I. Khalil    schedule 03.04.2015

Для этого вам нужно использовать 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);
}

Попробуй это. Это должно работать.

person Y.S    schedule 03.04.2015
comment
Да, я знаю, что есть метод openContactPhotoInputStream. Но это позволит получить фотографию в основном потоке, и я вызываю код Glide в адаптере и хочу, чтобы он обрабатывал получение изображения в фоновом потоке. - person Ahmed I. Khalil; 03.04.2015
comment
Я не использовал этот метод, так как он не работает ниже API 14. И я не вижу здесь проблемы; Glide не может быть быстрее, чем собственные классы фреймворка. Кроме того, Glide не гарантирует автоматическую обработку любых Uri. - person Y.S; 03.04.2015
comment
Я получаю java.lang.IllegalArgumentException: Invalid column data15, который связан с public static final String PHOTO = DATA15. Использование API 21. - person Henrique de Sousa; 15.04.2016

Я использую этот код, затем успешно работаю

 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);
person Nidhi Savaliya    schedule 14.09.2019

Самый быстрый и простой способ — сначала выяснить 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)
person azwethinkweiz    schedule 03.08.2020