Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exception java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder

Tags:

android

I am developing Android Application and After published it on play store, I facing this problem many time.

Exception java.lang.IndexOutOfBoundsException: Inconsistency detected.Invalid view holder adapter positionViewHolder{2f9d83c position=11 id=-1, oldPos=-1, pLpos:-1 no parent}
android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition (RecyclerView.java:5297)
android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline (RecyclerView.java:5479)
android.support.v7.widget.GapWorker.prefetchPositionWithDeadline (GapWorker.java:282)
android.support.v7.widget.GapWorker.flushTaskWithDeadline (GapWorker.java:336)
android.support.v7.widget.GapWorker.flushTasksWithDeadline (GapWorker.java:349)
android.support.v7.widget.GapWorker.prefetch (GapWorker.java:356)
android.support.v7.widget.GapWorker.run (GapWorker.java:387)
android.os.Handler.handleCallback (Handler.java:815)
android.os.Handler.dispatchMessage (Handler.java:104)
android.os.Looper.loop (Looper.java:207)
android.app.ActivityThread.main (ActivityThread.java:5737)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:789)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:679)

I searched for long time about this problem and applying many Solution but the Exception still raised

One of Solution that I applied was Making Custom LinearLayoutManager:

public class WrapContentLinearLayoutManager  extends LinearLayoutManager {

    public WrapContentLinearLayoutManager(Context context) {
        super(context);
    }

    //... constructor
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            super.onLayoutChildren(recycler, state);
            setAutoMeasureEnabled(false);
        } catch (IndexOutOfBoundsException e) {
            Log.e("Error", "IndexOutOfBoundsException in RecyclerView happens");
        }
    }

    public WrapContentLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
        setAutoMeasureEnabled(false);
    }

    public WrapContentLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        setAutoMeasureEnabled(false);
    }

    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }
}

In My Application depends on RecyclerView with Load more and Swipe to Refresh features

I using Volley to get Data from web Service for Load more and Swipe to Refresh

To Load More Item in RecyclerView, inside the call back of Volley Response I append the new Items in to ArrayList and notify the adapter Like this:

    final int positionStart = list.size() - 1;
    final int itemCount = response.length();
    for (int i=0;i<response.length();i++)
    {
        try 
        {
            obj = response.getJSONObject(i);
            list.add(new ItemEntry(obj.getInt("Id"),obj.getString("Title").trim()obj.getBoolean("isFavorite"));
        } catch (JSONException e) {
            e.printStackTrace();
        }
        if(index == 0)
        { 
            // for first Initialize for adapter 
            adapter = new MyAdapter(latestEntryList,getActivity());
            adapter.setOnItemClickCallBack(mainFragment);
            recyclerView.setAdapter(adapter);
        }
        else
        {
            // for load more items
            recyclerView.post(new Runnable() 
            {
                @Override
                public void run() 
                {
                   adapter.updateItems(positionStart,itemCount);
                }
            });
        }
    }

The Code for Adapter.UpdateItems

  public void updateItems(int positionStart,int itemCount) {
        itemsCount = getItemCount();
        this.notifyItemRangeInserted(positionStart,itemCount);
    }

In case of load More I do the following; in OnRefresh

   public void onRefresh() {
        listFragment.LatestEntry(0,false);
// 0 the index and False isAppend to list of not
    } 

The code for LatestEntry is:

   private void LatestEntry(final int index, final boolean isAppend)
        {
        String customerId = "123";
        String uri = String.format(BASE_URL+"/api/Entry/LatestEntry?customerId=%s&index=%s",customerId,index);

            JsonArrayRequest LatestEntryJsonArrayRequest = new JsonArrayRequest(Request.Method.GET, uri,
                null, new Response.Listener<JSONArray>() {
                @Override
                public void onResponse(JSONArray response) 
                   {
                        JSONObject obj = new JSONObject();
                        if(response.length() > 0 && !isAppend) 
                        {
                            int startPosition = 0;
                            int preSize;

                            if(adapter != null)
                                preSize = adapter.entries.size();
                            else
                                preSize = 0;
                            if(preSize > 0 && EntryList.size() > 0) {
                                EntryList.clear();
                                adapter.entries.clear();
                                adapter.notifyItemRangeRemoved(startPosition, preSize);
                            }
                        }

                        final int positionStart = EntryList.size() - 1;
                        final int itemCount = response.length();
                        for (int i=0;i<response.length();i++)
                        {
                            try {
                                obj = response.getJSONObject(i);
                                list.add(new ItemEntry
                               (obj.getInt("Id"),obj.getString("Title").trim()
                                  obj.getBoolean("isFavorite"));

                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }

                        if(index == 0) 
                        { // for first Initialize for adapter 
                            adapter = new MyAdapter(latestEntryList,getActivity());
                            adapter.setOnItemClickCallBack(mainFragment);
                            recyclerView.setAdapter(adapter);
                        }
                        else
                        {
                            // for load more items
                            recyclerView.post(new Runnable() 
                            {
                                @Override
                                public void run() {
                                   adapter.updateItems(positionStart,itemCount);
                                }
                            });
                        }
                    }
                    setListShownNoAnimation(true);
                    if(mainFragment.swipeRefreshLayout.isRefreshing())
                      mainFragment.swipeRefreshLayout.setRefreshing(false);
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Toast.makeText(MyApplication.getAppContext(),error.getMessage(),Toast.LENGTH_LONG);
                }
            }
            );

            LatestEntryJsonArrayRequest.setTag("LatestEntry");
            KidsSingleton.getInstance().addToRequestQueue(LatestEntryJsonArrayRequest);
        }

Please note the Following:

  1. The Recyclerview Version is: recyclerview-v7:25.3.0
  2. I have Three RecylerView in ViewPager
  3. Each ViewPager Contain Fragment and The Fragment Contain ListFragment with RecylerView.
  4. There are one Adapter for all RecylcrViews.

Can any one help to overcome this issue?

like image 259
Mohammed K. Abuhalib Avatar asked Mar 28 '17 08:03

Mohammed K. Abuhalib


1 Answers

I have similiar situation - viewpagers as items of recycler view. While adding\removing items there was some animation for these "waiting to be removed" items. So when I was calling remove item, and animation still was acting I've got:

IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling

To avoid this exception I've created a handler to delay removing items. BUT! This is wrong. If you've changed data of adapter RV should be immediately without any handler or multithreading notified about these changes. So, think about changing this way into something where updates won't need separate threads.

like image 57
Nazacheres Avatar answered Jan 03 '23 19:01

Nazacheres