用于 base64 图像的自定义 Glide ModelLoader
Custom Glide ModelLoader for base64 images
我有来自某些 SOAP API 的图像,这些图像是用 base64 编码的。为了直接使用 Glide 加载它们,我决定编写自定义 ModelLoader。
Glide 版本: 4.3.1
滑翔载重线:
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))
}
}
加载器:
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.run(GlideExecutor.java:413)
我想我缺少一些东西来让它发挥作用。
感谢 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
}
}
我有来自某些 SOAP API 的图像,这些图像是用 base64 编码的。为了直接使用 Glide 加载它们,我决定编写自定义 ModelLoader。
Glide 版本: 4.3.1
滑翔载重线:
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))
}
}
加载器:
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.run(GlideExecutor.java:413)
我想我缺少一些东西来让它发挥作用。
感谢 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
}
}