Issue
I'm loading a list of URLs using Glide as follows :
@Override
public void onBindViewHolder(@NonNull SearchViewHolder holder, int position) {
Glide.with(context)
.load(arrayList.get(position))
.diskCacheStrategy(DiskCacheStrategy.ALL)
.error(R.drawable.transparent_icon)
.override(Constants.THUMBNAIL_SIZE, Constants.THUMBNAIL_SIZE)
.fitCenter()
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
isBrokenUrl = true;
arrayList.remove(holder.getBindingAdapterPosition()); //ISSUE IS HERE
notifyItemRangeChanged(holder.getBindingAdapterPosition(), getItemCount());
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
isBrokenUrl = false;
return false;
}
}).into(holder.image);
holder.image.setOnClickListener(v -> itemStringClickListener.onItemClicked(arrayList.get(position)));
}
In the list there are a broken URL, I would like to remove the item with these broken url from RecyclerView
:
arrayList.remove(holder.getBindingAdapterPosition());
notifyItemRangeChanged(holder.getBindingAdapterPosition(), getItemCount());
It's work, but if I scroll too fast, the app crash with :
com.bumptech.glide.load.engine.CallbackException: Unexpected exception thrown by non-Glide code
at com.bumptech.glide.load.engine.EngineJob.callCallbackOnLoadFailed(EngineJob.java:175)
at com.bumptech.glide.load.engine.EngineJob$CallLoadFailed.run(EngineJob.java:402)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:236)
at android.app.ActivityThread.main(ActivityThread.java:7889)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:600)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
Caused by: java.lang.ArrayIndexOutOfBoundsException: length=109; index=-1
at java.util.ArrayList.remove(ArrayList.java:506)
at maa.utils.adapters.SearchedStickersAdapter$1.onLoadFailed(SearchedStickersAdapter.java:63)
at com.bumptech.glide.request.SingleRequest.onLoadFailed(SingleRequest.java:683)
at com.bumptech.glide.request.SingleRequest.onLoadFailed(SingleRequest.java:651)
at com.bumptech.glide.load.engine.EngineJob.callCallbackOnLoadFailed(EngineJob.java:173)
at com.bumptech.glide.load.engine.EngineJob$CallLoadFailed.run(EngineJob.java:402)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:236)
at android.app.ActivityThread.main(ActivityThread.java:7889)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:600)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
Solution
The code failed because getBindingAdapterPosition returned -1. That view holder wasn't bound to any position. It seems like you're trying to do something asynchronous here. If so, you should NOT be passing the view holder or any of the views in it to any callbacks. Instead, you should pass the position and/or the data item at that index when you make the asynchronous call, and NOT query the holder during the callback. This is because the binding can change between then and when the callback occurs. If you don't do it that way, even when it "works" it may be performing actions on the wrong index.
Actually since you're doing this in onBindViewHolder, replacing that with just position
in both places you use it should make it work.
Answered By - Gabe Sechan