I have a custom Glide model to calculate the center crop for image. The same model is used for getting images from server and also from local storage.
Here's the model interface:
interface CenterCropImageInformation {
fun getCenterCropUri(context: Context, @Px width: Int, @Px height: Int): Uri
}
And here is its ModelLoader that extends from BaseGlideUrlLoader
:
class CenterCropImageInformationLoader private constructor(
context: Context,
concreteLoader: ModelLoader<GlideUrl, InputStream>,
modelCache: ModelCache<CenterCropImageInformation, GlideUrl>?
) : BaseGlideUrlLoader<CenterCropImageInformation>(concreteLoader, modelCache) {
private val applicationContext: Context = context.applicationContext
override fun getUrl(
model: CenterCropImageInformation, width: Int,
height: Int, options: Options
): String {
return model.getCenterCropUri(applicationContext, width, height).toString()
}
override fun handles(centerCropImageInformation: CenterCropImageInformation): Boolean {
return true
}
/**
* The default factory for [CenterCropImageInformation]s.
*/
class Factory(
private val applicationContext: Context,
private val modelCache: ModelCache<CenterCropImageInformation, GlideUrl>?
) : ModelLoaderFactory<CenterCropImageInformation, InputStream> {
override fun build(
multiFactory: MultiModelLoaderFactory
): ModelLoader<CenterCropImageInformation, InputStream> {
val modelLoader = multiFactory.build(GlideUrl::class.java, InputStream::class.java)
return CenterCropImageInformationLoader(applicationContext, modelLoader, modelCache)
}
override fun teardown() {}
}
}
This works fine for images with http/https
scheme, but it doesn't work for file
scheme - the one used for loading images from local device storage.
I had a look into Glide's source code and the closest ModelLoader that sounds like an option is UriLoader
, but the issue that that one doesn't support custom models. It only supports Uri
.
The optimal solution would be to use a pre-existing ModelLoader that is bundled with Glide, but unless I missed it, I couldn't find any that suits my needs. If that's really the case, how do I implement such ModelLoader?
I figured it out after reading Glide's ModelLoaders tutorial. The key is by delegating the loading to a ModelLoader that knows how to handle file
and http/https
scheme.
What I had to do is to implement ModelLoader
interface directly instead of extending BaseGlideUrlLoader
. We already know that Glide's built-in UriLoader
can handle both file
and http/https
scheme so we delegate to it. Now to get an instance of UriLoader
we use the MultiModelLoaderFactory
that is passed to our factory's build
method. The default Glide configuration registers UriLoader
for Uri
+ InputStream
pair.
class CenterCropImageInformationLoader(
private val modelLoader: ModelLoader<Uri, InputStream>,
private val modelCache: ModelCache< CenterCropImageInformation, Uri>
) : ModelLoader<CenterCropImageInformation, InputStream> {
override fun buildLoadData(
model: CenterCropImageInformation,
width: Int,
height: Int,
options: Options
): ModelLoader.LoadData<InputStream>? {
val uri: Uri = modelCache.get(model, width, height) ?: model.getUri(model, width, height)
modelCache.put(model, width, height, uri)
return modelLoader.buildLoadData(uri, width, height, options)
}
override fun handles(model: CenterCropImageInformation): Boolean = true
class Factory(
private val applicationContext: Context,
private val modelCache: ModelCache<CenterCropImageInformation, Uri>
) : ModelLoaderFactory<CenterCropImageInformation, InputStream> {
override fun build(
multiFactory: MultiModelLoaderFactory
): ModelLoader<CenterCropImageInformation, InputStream> {
val modelLoader = multiFactory.build(Uri::class.java, InputStream::class.java)
return CenterCropImageInformationLoader(applicationContext, modelLoader, modelCache)
}
override fun teardown() {}
}
}
As we can see we no longer extend BaseGlideUrlLoader
. Instead we implement ModelLoader
interface and in buildLoadData()
implementation we try to get the URI from cache (this is similar to what BaseGlideUrlLoader
is doing) and then we call buildLoadData()
on the ModelLoader
that we passed to the constructor, which happens to be an instance of UriLoader
as I mentioned earlier thanks to MultiModelLoaderFactory
.
It's really surprising that this type of ModelLoader is not part of Glide built-in model loaders.