代码之家  ›  专栏  ›  技术社区  ›  Aleksander Mielczarek

base64图像的自定义Glide ModelLoader

  •  6
  • Aleksander Mielczarek  · 技术社区  · 7 年前

    我有一些用base64编码的SOAP API的图像。为了直接用Glide加载它们,我决定编写自定义ModelLoader。

    Glide版本 : 4.3.1

    滑动载重线 :

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

    滑动模块 :

    @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))
        }
    }
    

    装载机 :

    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
        }
    
    }
    

    堆栈跟踪/LogCat :

    com.bumptech.glide.Registry$NoSourceEncoderAvailableException: Failed to find source encoder for data class: class [B
        at com.bumptech.glide.Registry.getSourceEncoder(Registry.java:534)
        at com.bumptech.glide.load.engine.DecodeHelper.getSourceEncoder(DecodeHelper.java:232)
        at com.bumptech.glide.load.engine.SourceGenerator.cacheData(SourceGenerator.java:74)
        at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:45)
        at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:298)
        at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:268)
        at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:229)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at java.lang.Thread.run(Thread.java:764)
        at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:413)
    

    我觉得我错过了一些让它发挥作用的东西。

    1 回复  |  直到 5 年前
        1
  •  2
  •   Eric    3 年前

    感谢来自的评论 https://github.com/bumptech/glide/issues/2677 我找到了解决办法。

    @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 MyImageDataFetcher(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
        }
    
    }