Custom Glide ModelLoader para imágenes base64

Tengo imágenes de alguna API SOAP que están codificadas en base64. Para cargarlos directamente con Glide, decidí escribir ModelLoader personalizado.

Versión Glide : 4.3.1

Línea de carga Glide :

GlideApp.with(activity) .load(Data().apply { code = licensePlateData.licensePlateImgId }) .into(view.imageLicense) 

GlideModule :

 @GlideModule class MyAppGlideModule : AppGlideModule() { override fun registerComponents(context: Context, glide: Glide, registry: Registry) { super.registerComponents(context, glide, registry) val app = context.applicationContext as MyApplication registry.append(Data::class.java, ByteArray::class.java, MyImageLoaderFactory(app.api)) } } 

Cargador :

 class MyImageLoaderFactory(private val api: Api) : ModelLoaderFactory<Data, ByteArray> { override fun teardown() { } override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<Data, ByteArray> { return MyImageLoader(api) } } class MyImageLoader(private val api: Api) : ModelLoader<Data, ByteArray> { override fun buildLoadData(model: Data, width: Int, height: Int, options: Options): ModelLoader.LoadData<ByteArray> { val key = "code:${model.code};width:$width;height:$height" return ModelLoader.LoadData<ByteArray>(ObjectKey(key), MyImageDataFetcher(api, GetImageRequest().apply { data = model })) } override fun handles(model: Data): Boolean { return true } } class MyImageDataFetcher(private val api: Api, private val request: GetImageRequest) : DataFetcher<ByteArray> { private val disposables = CompositeDisposable() override fun cleanup() { disposables.clear() } override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in ByteArray>) { api.getImage(request) //soap request .map { it.image.decodeBase64() } //translate String to ByteArray .subscribe({ callback.onDataReady(it) }, { if (it is Exception) { callback.onLoadFailed(it) } else { callback.onLoadFailed(MyException(it)) } }) .addTo(disposables) } override fun cancel() { disposables.clear() } override fun getDataClass(): Class<ByteArray> { return ByteArray::class.java } override fun getDataSource(): DataSource { return DataSource.REMOTE } } 

Stack trace / LogCat :

com.bumptech.glide.Registry $ NoSourceEncoderAvailableException: no se pudo encontrar el codificador fuente para la class de datos: class [B en com.bumptech.glide.Registry.getSourceEncoder (Registry.java:534) en com.bumptech.glide.load.engine. DecodeHelper.getSourceEncoder (DecodeHelper.java:232) en com.bumptech.glide.load.engine.SourceGenerator.cacheData (SourceGenerator.java:74) en com.bumptech.glide.load.engine.SourceGenerator.startNext (SourceGenerator.java: 45) en com.bumptech.glide.load.engine.DecodeJob.runGenerators (DecodeJob.java:298) en com.bumptech.glide.load.engine.DecodeJob.runWrapped (DecodeJob.java:268) en com.bumptech.glide .load.engine.DecodeJob.run (DecodeJob.java:229) en java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1162) en java.util.concurrent.ThreadPoolExecutor $ Worker.run (ThreadPoolExecutor.java:636 ) en java.lang.Thread.run (Thread.java:764) en com.bumptech.glide.load.engine.executor.GlideExecutor $ DefaultThreadFactory $ 1.run (GlideExecutor.java:413)

Creo que me falta algo para que funcione.

Gracias al comentario de https://github.com/bumptech/glide/issues/2677 encontré la solución.

 @GlideModule class MyAppGlideModule : AppGlideModule() { override fun registerComponents(context: Context, glide: Glide, registry: Registry) { super.registerComponents(context, glide, registry) val app = context.applicationContext as MyApplication registry.append(Data::class.java, ByteBuffer::class.java, MyImageLoaderFactory(app.api)) } } class MyImageLoaderFactory(private val api: Api) : ModelLoaderFactory<Data, ByteBuffer> { override fun teardown() { } override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<Data, ByteBuffer> { return MyImageLoader(api) } } class MyImageLoader(private val api: Api) : ModelLoader<Data, ByteBuffer> { override fun buildLoadData(model: Data, width: Int, height: Int, options: Options): ModelLoader.LoadData<ByteBuffer> { val key = "code:${model.code};width:$width;height:$height" return ModelLoader.LoadData<ByteBuffer>(ObjectKey(key), MyImageDataFetcher(api, GetImageRequest().apply { data = model })) } override fun handles(model: Data): Boolean { return true } } class ClmImageDataFetcher(private val api: Api, private val request: GetImageRequest) : DataFetcher<ByteBuffer> { private val disposables = CompositeDisposable() override fun cleanup() { disposables.clear() } override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in ByteBuffer>) { api.getImage(request) .map { it.image.decodeBase64() } .subscribe({ callback.onDataReady(ByteBuffer.wrap(it)) }, { if (it is Exception) { callback.onLoadFailed(it) } else { callback.onLoadFailed(MyException(it)) } }) .addTo(disposables) } override fun cancel() { disposables.clear() } override fun getDataClass(): Class<ByteBuffer> { return ByteBuffer::class.java } override fun getDataSource(): DataSource { return DataSource.REMOTE } } 
  • Interoperabilidad de Kotlin-JS: uso de construcciones de lenguaje
  • Cómo convertir String a Long en Kotlin?
  • Cómo volver a vincular al service de reproductor de música bind ya en ejecución
  • Los enlaces de fábrica Kodein están lanzando NotFoundException
  • La mejor forma de traducir este código java a kotlin
  • Cómo hacer que la key principal sea autoincrement para Room Persistence lib
  • ¿Cómo configurar la encoding de fuente de Kotlin en Gradle?
  • ¿Existe un equivalente de Kotlin para la biblioteca AssertJ?
  • ¿Cómo puedo convertir una parte del file fuente de Java a Kotlin?
  • El procesador de anotación de Kotlin genera un error de time de compilation al usar Room con Android Studio 3.0 beta7
  • cómo evitar el cheque nulo