Glide Version: 4.9.0
I tried to customized the RequestBuilder
class, to have a customized builder, in this case I want to customize RequestBuilder#addListener
function to have a @FunctionalInterface
or Function0<R>
as argument and to be functional for Java 8.
I've tried to read documentation about @GlideExtensions
here & the difference between:
@GlideOption
: for adding customizedRequestOptions
as long first parameter is extended fromBaseRequestOptions<*>
in this caseRequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
@GlideType
: Definitely creating different type of drawable, not doing much of configuration, therefore I didn't try it.
Due to RequestListener
doesn't have @FunctionalInterface
annotation, I ended-up using both Kotlin & Java 8 to achieve the lambda-like parameter function.
I've created a SafeGlideApp
class to handle context related problem using RequestManager inside onCreate & checking if the activity isFinishing
class SafeGlideApp {
companion object {
private lateinit var rm: RequestManager
private lateinit var target: ViewTarget<ImageView, Drawable>
/**
* This is mandatory to setup inside onCreate() on any Activity/Fragment
* @param Activity receive the current activity if not finishing
*/
@JvmStatic
fun init(activity: Activity) {
rm = Glide.with(activity)
}
@JvmStatic
fun destroy() {
target.clearOnDetach()
rm.pauseAllRequests()
rm.clear(target)
}
@JvmStatic
fun with(activity: Activity, safeAction: (RequestManager) -> ViewTarget<ImageView, Drawable>) {
init(activity)
activity.safeRunnable {
check(::rm.isInitialized)
target = safeAction(rm)
}
}
}
}
Here's my desired functions to call custom listener on SafeGlideApp.
Activity.java
SafeGlideApp.with(this, requestManager -> requestManager
.load(url)
.listener(
Helpers.wrapper(() -> doSomething()),
Helpers.wrapper(() -> doSomething())
)
.into(imageView)
);
In order to have an interop between kotlin-java, I created this wrapper inspired by everyone in stackoverflow. So, in the invocation calls, cleaner and simplier.
Helpers.java
/**
* This lambda wrapper function takes nothing as argument and return Unit
* E.g: f(wrapper(() -> doSomethingVoid()))
*
* @param callable
* @return Unit
*/
public static Function0<Unit> wrapper(Runnable callable) {
return () -> {
callable.run();
return Unit.INSTANCE;
};
}
And here's my approach so far to customize the RequestBuilder
.
My generic listener class
CustomListener.kt
fun <T : Any> makeLoadingRequestListener(
failedAction: (() -> Unit)?,
readyAction: (() -> Unit)?
): RequestListener<T> = object : RequestListener<T> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<T>?, isFirstResource: Boolean): Boolean {
failedAction?.invoke()
return false
}
override fun onResourceReady(resource: T, model: Any?, target: Target<T>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
readyAction?.invoke()
return false
}
}
Troubleshooting
- Using explicit type Drawable will have a compile-error:
e: [kapt] An exception occurred: java.lang.IllegalArgumentException:
@GlideOption methods must take a BaseRequestOptions<?> object as
their first parameter, but the first parameter in
MyAppGlideExtension#listener(RequestBuilder, Unit, Unit) is
RequestBuilder<Drawable>
Here's my extension class MyGlideExtension.kt
@GlideExtension
open class MyAppGlideExtension private constructor() {
companion object {
@JvmStatic
@GlideOption
fun listener(
options: RequestBuilder<Drawable>,
failedAction: () -> Unit,
readyAction: () -> Unit
): BaseRequestOptions<Drawable> {
return options.addListener(makeLoadingRequestListener<Drawable>(
failedAction = failedAction,
readyAction = readyAction
))
}
}
}
As stated in this issue #3552 & #4030. FlickrGlideExtension
example is using Java. Also, the author stated to use ?
wildcard in Kotlin, in fact that's not supported in Kotlin.
- I've also try the java approach like this, and also have a compile-error
@GlideExtension
public final class MyGlideExtension {
private MyGlideExtension GlideExtension() {
return this;
}
@GlideOption
public static BaseRequestOptions<?> test(RequestBuilder<?> requestBuilder,
Function0<Unit> failed,
Function0<Unit> ready) {
return requestBuilder.listener(new RequestListener<Object>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Object> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Object resource, Object model, Target<Object> target, DataSource dataSource, boolean isFirstResource) {
return false;
}
});
}
}
- Unfortunately, I didn't know how to use projection to solve this issue out-projected type prohibits for
RequestBuilder#addListener
function
@GlideExtension
open class MyAppGlideExtension private constructor() {
companion object {
@JvmStatic
@GlideOption
fun listener(
options: RequestBuilder<*>,
failedAction: () -> Unit,
readyAction: () -> Unit
): BaseRequestOptions<*> {
return options.addListener(makeLoadingRequestListener<Object>(
failedAction = failedAction,
readyAction = readyAction
))
}
}
}
- Manually extending
RequestManager
&RequestBuilder
class CustomRequestManager(
glide: Glide, lifecycle: Lifecycle,
treeNode: RequestManagerTreeNode, context: Context
) : RequestManager(glide, lifecycle, treeNode, context) {
override fun load(string: String?): CustomRequestBuilder<Drawable> {
return super.load(string) as CustomRequestBuilder<Drawable>
}
}
class CustomRequestBuilder<TranscodeType : Any>(
glide: Glide,
requestManager: RequestManager,
transcodeClass: Class<TranscodeType>,
context: Context
) : RequestBuilder<TranscodeType>(glide, requestManager, transcodeClass, context) {
fun listener(failedAction: () -> Unit, readyAction: () -> Unit): RequestBuilder<TranscodeType> {
return addListener(makeLoadingRequestListener<TranscodeType>(
failedAction = failedAction,
readyAction = readyAction
))
}
}
but failed when casting GlideApp.with(this) as CustomRequestManager
and throws compile-error: GlideRequest cannot be casted to CustomRequestManager
Last but not least.
- Is there any approach possible to extends/customize
RequestBuilder
class? - Or is this possible creating a helper class without extending
RequestBuilder
to use & wrapRequestListener<R>
interface to be functional?