Home > database >  java.lang.IndexOutOfBoundsException : Trying to remove an item with broken URL from RecyclerView
java.lang.IndexOutOfBoundsException : Trying to remove an item with broken URL from RecyclerView

Time:11-09

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

CodePudding user response:

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.

  • Related