Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ArrayIndexOutOfBoundsException when populating RecyclerView

I am getting an error every so often when I try to populate my RecyclerView but the error seems to happening internally in the StaggeredGridLayoutManager.

I populate the RecyclerView from my database then add the images to my adapter using this

List<WImage> images;

public void addImages(LinkedHashMap<Integer,WImage> newImages){
    for(Map.Entry<Integer,WImage> entry : newImages.entrySet()){
        addImage(entry.getValue(),entry.getKey());
    }
}

public void addImage(WImage image, int position){
    images.add(position,image);
    notifyItemRangeInserted(position, 1);

}

What is weird is when the app first loads it is fine and displays everything correctly but if I replace what was currently in the RecyclerView with a different set of images based on a database query then come back with the initial images again I get this error.

java.lang.ArrayIndexOutOfBoundsException: src.length=16 srcPos=0 dst.length=0 dstPos=0 length=16
            at java.lang.System.arraycopy(Native Method)
            at android.support.v7.widget.StaggeredGridLayoutManager$LazySpanLookup.ensureSize(StaggeredGridLayoutManager.java:2277)
            at android.support.v7.widget.StaggeredGridLayoutManager$LazySpanLookup.offsetForAddition(StaggeredGridLayoutManager.java:2323)
            at android.support.v7.widget.StaggeredGridLayoutManager.handleUpdate(StaggeredGridLayoutManager.java:1280)
            at android.support.v7.widget.StaggeredGridLayoutManager.onItemsAdded(StaggeredGridLayoutManager.java:1253)
            at android.support.v7.widget.RecyclerView$5.dispatchUpdate(RecyclerView.java:406)
            at android.support.v7.widget.RecyclerView$5.onDispatchSecondPass(RecyclerView.java:422)
            at android.support.v7.widget.AdapterHelper.consumePostponedUpdates(AdapterHelper.java:119)
            at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1896)
            at android.support.v7.widget.RecyclerView.resumeRequestLayout(RecyclerView.java:1101)
            at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:155)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
            at android.view.Choreographer.doCallbacks(Choreographer.java:574)
            at android.view.Choreographer.doFrame(Choreographer.java:543)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5086)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
            at dalvik.system.NativeStart.main(Native Method)

it goes through all the images just fine but when its ready to call onBindViewHolder again it crashes.

When I change the data in the RecyclerView I remove everything first with this before populating with the new data from the query

public void removeAll(){
    images.clear();
    notifyDataSetChanged();
}

I also tried inserting them all at once and then calling notifyItemRangeInserted with the start position and the total number of images but it still crashsed with the same error.

EDIT

it seems to happen when I hit about 20 or so items that I need to put into the Grid. If I just use notifyDatasetChanged it seems to work fine but I want to use notifyItemRangeInserted so I get the nice insert animation

Has anyone else seen this problem?

EDIT2

here is my onCreateViewHolder

@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, final int i) {

    View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.image_normal_grid_cell, viewGroup, false);
    return new ViewHolder(v);
}

and my onBindViewHolder

@Override
public void onBindViewHolder(final ViewHolder holder, final int i) {
    final WImage image = images.get(i);
    holder.v.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            listener.onItemClick(v,i,image);
        }
    });

    holder.heart.setActivated(image.getLikeDislike());

    holder.heart.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Uri.Builder ub = SyncProvider.CONTENT_ID_URI_BASE.buildUpon();
            ub.appendPath(String.valueOf(image.getId()));
            ContentValues values = new ContentValues();

            if(image.getLikeDislike()){
                values.put(SyncProvider.LIKE,0);
                holder.heart.setActivated(false);
                image.setLikeDislike(false);
            }else{
                values.put(SyncProvider.LIKE,1);
                holder.heart.setActivated(true);
                image.setLikeDislike(true);
            }
            values.put(SyncProvider.IMAGE_CHANGED,1);
            context.getContentResolver().update(ub.build(),values,null,null);
        }
    });
    if(cancelPotentialDownload(image.getUri(),holder.imageview)){
        LoadImageTask task = new LoadImageTask(holder.imageview,image.getThumbUri());
        holder.imageview.setImageDrawable(new AsyncBitmap(task));
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }
}

EDIT 3

here is what I do when I replace the adapter

on selected index change of my nav drawer I init my loader to make a query

public void initLoader(int page){
    if(currentPage != page && mAdapter != null){
        mAdapter.removeAll();
        hashImages.clear();
        getLoaderManager().destroyLoader(currentPage);
    }
    currentPage = page;
    getLoaderManager().initLoader(page,null,this);
}

then in my onLoadFinished of my loader I add the objects to the adapter

@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
    if(cursor != null && cursor.moveToFirst()){
            do{
                WImage image = new WImage(cursor);
                if(!hashImages.containsKey(image.getId())){
                    newImages.put(tempHash.size(),image);
                }else if(image.getImageChanged()){
                    update = true;
                }
                int pos = tempHash.size();
                tempHash.put(image.getId(),pos);
            }while(cursor.moveToNext());
        }

    if(newImages.size() > 0){
            mAdapter.addImages(newImages);
        }
like image 267
tyczj Avatar asked Sep 18 '25 00:09

tyczj


1 Answers

This bug has been solved by google in version 21.0.2:

compile "com.android.support:support-v4:21.0.2"
compile "com.android.support:recyclerview-v7:21.0.2"
like image 167
eliocs Avatar answered Sep 19 '25 14:09

eliocs