14

Before moving ahead i search about this bug on google and i found the so many answer which are available but my scenario is different than their situation.

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3

I am using recyclerview to display the poll questions and every question has a timer, Item will removed from the list when timer runs out.

Exception is occurring when the timer runs out but in only some rare scenario when less time (for ex 100ms) is remaining. so in that case may be recyclerview is inflating item and at that same time, timer rans out and recyclerview try to remove that item.

Bug is when timer removing item i am getting the exception.

So I solved it after removing the elements from data set when 1 or less seconds is remaining for that poll. So it will not add item in list and that will run the timer.

If you want to produce the bug just start countdown timer in bindView and once the timer runs out remove that particular item. You have to make timer below 500ms.

So now Everything works perfectly. Crashing on a rare scenario was solved but I don't want to remove the element from dataset even if less time is remaining. Please give me the proper solution for this bug.

Edited

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 1(offset:1).state:2 com.lsjwzh.widget.recyclerviewpager.RecyclerViewPager{182da0c VFED..... .F....ID 0,0-720,1024 #7f0a01a1 app:id/recycler_view}, adapter:com.lsjwzh.widget.recyclerviewpager.RecyclerViewPagerAdapter@c9403f, layout:android.support.v7.widget.LinearLayoutManager@6fdbd0c, context:com.bitpoll.polls.MainActivity@b40f4c0
     android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5817)
     android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5752)
     android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5748)
     android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2232)
     android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1559)
     android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1519)
     android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:614)
     android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3812)
     android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3529)
     android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:4082)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.support.v4.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:606)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.RelativeLayout.onLayout(RelativeLayout.java:1189)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.support.constraint.ConstraintLayout.onLayout(ConstraintLayout.java:1855)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.support.design.widget.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:132)
     android.support.design.widget.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:42)
     android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:1361)
     android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:894)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.RelativeLayout.onLayout(RelativeLayout.java:1189)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.FrameLayout.layoutChildren(FrameLayout.java:383)
     android.widget.FrameLayout.onLayout(FrameLayout.java:321)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.LinearLayout.setChildFrame(LinearLayout.java:1982)
     android.widget.LinearLayout.layoutVertical(LinearLayout.java:1826)
     android.widget.LinearLayout.onLayout(LinearLayout.java:1735)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.FrameLayout.layoutChildren(FrameLayout.java:383)
     android.widget.FrameLayout.onLayout(FrameLayout.java:321)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.LinearLayout.setChildFrame(LinearLayout.java:1982)
     android.widget.LinearLayout.layoutVertical(LinearLayout.java:1826)
     android.widget.LinearLayout.onLayout(LinearLayout.java:1735)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.FrameLayout.layoutChildren(FrameLayout.java:383)
     android.widget.FrameLayout.onLayout(FrameLayout.java:321)
     com.android.internal.policy.DecorView.onLayout(DecorView.java:753)
     android.view.View.layout(View.java:17969)

Updated

This is the adapter code.

public class PollAdapter extends RecyclerView.Adapter<PollAdapter.PollViewHolder> {

    // .... 

    @Override
    public void onBindViewHolder(@NonNull PollViewHolder holder, int position) {

    // ...

        if (holder.countDownTimer != null) {
            holder.countDownTimer.cancel();
        }

        // CountDown to set Timer in poll
        holder.countDownTimer = new CountDownTimer(<Timer to be finished.>, 1000) {

            public void onTick(long millisUntilFinished) {
                holder.time.setText("<Remaining time>");
            }

            public void onFinish() {
                removeAt(holder.getAdapterPosition());
            }

        }.start();
    }

    @Override
    public int getItemCount() {
        return pollList.size();
    }

    // To Remove Poll after Time finished
    public void removeAt(int position) {
    pollList.remove(position);
        notifyItemRemoved(position);
    }
}
Moinkhan
  • 12,732
  • 5
  • 48
  • 65
  • Are doing any manipulations on a non-UI thread? Posting the full stack trace may help. – Cheticamp Jun 28 '18 at 15:03
  • @Cheticamp added full stacktrace. – Moinkhan Jun 29 '18 at 07:14
  • Is it possible for you to share your code? – Rahul Shukla Jul 02 '18 at 07:38
  • @RahulShukla there is no extra code. Just consider normal recycler view adapter. Extra part is i starting the timer in `onBindView` method and removing the element on it `onFinished` method with calling `notifyItemRemoved(pos)`. Everything work perfectly except, while i am adding element timer is below 1 second then it crashed. – Moinkhan Jul 02 '18 at 12:00
  • @Moinkhan, well anything below 1sec is practically not going to visible to the user. Anyways, are you creating new timers for every item in onBindView()? – Rahul Shukla Jul 03 '18 at 02:07
  • You have to provide adapter's code and timer also. – ADM Jul 04 '18 at 10:24
  • yes, it will be helpful if you provide the code. – Ranjan Jul 05 '18 at 04:59
  • @ADM added the adapter code. – Moinkhan Jul 05 '18 at 07:12
  • NOT a good idea to have `CountDownTimer` for each row .I need to look further into your code meanwhile you can have a look at [This](https://stackoverflow.com/questions/31059251/how-to-handle-multiple-countdown-timers-in-listview). If nothing make sense to you then ask, i'll try to help when i have time. – ADM Jul 05 '18 at 07:28
  • @Moinkhan The adapter which you have posted right now does it work or does it crash? – MadScientist Jul 05 '18 at 11:07

9 Answers9

9

This error comes when the Adapter of the RecyclerView is not being notified for a change.

To tackle this error, look for all the points where data is being changed in the underlying Adapter there is no straight forward solution to this as this is an internal crash, thrown by the RecyclerView

What you could do is:

  1. Since you're adding, call notifyItemInserted when you add
  2. and since you're removing as well call notifyItemRemoved when you remove

To test this without any optimizations you can also call notifyDataSetChanged both the time.

MadScientist
  • 2,134
  • 14
  • 27
  • 1
    I tried `notifyItemRemoved()` still getting that crash. But i haven't used `notifyItemAdded()` i will try it and get back to you. – Moinkhan Jun 26 '18 at 07:27
  • sure, remember you have to call it from both the places, adding and removing and best possible even for changing. The adapter should always have the most updated data. it fails sometime only because it fails when recyclerview checks for inconsistencies. – MadScientist Jun 26 '18 at 07:29
  • there is no such a method as `notifyItemAdded()`. – Moinkhan Jun 26 '18 at 09:04
  • `notifyItemInserted` not using an IDE while typing this, so name mismatches can occur. – MadScientist Jun 26 '18 at 09:06
  • I tried `notifyItemRangeInserted` and `notifyItemInserted`, but still i am getting the exception. – Moinkhan Jun 26 '18 at 11:41
5

Use custom LinearLayoutManager

public class CustLinearLayoutManager extends LinearLayoutManager {

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

public CustLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
    super(context, orientation, reverseLayout);
}

public CustLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
}

// Something is happening here

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

Use:-

CustLinearLayoutManager clm = new CustLinearLayoutManagerr(mContext);
recyclerView.setLayoutManager(clm);

I hope it ll works

Andrii Omelchenko
  • 13,183
  • 12
  • 43
  • 79
Nikunj Paradva
  • 15,332
  • 5
  • 54
  • 65
3

This seems to be the case of adding and removing the items concurrently. I think, it can be solved by executing all the tasks sequentially. Since your codes are not related to each other, I would suggest you to use recycler.post(new Runnable(){}); and put your code inside the run() method, adding/removing elements, and then notifyDatasetChanged() should be done inside this run() method. Let me know the results.

CoolMind
  • 26,736
  • 15
  • 188
  • 224
Suhaib Roomy
  • 2,501
  • 1
  • 16
  • 22
  • This worked for me. I was updating the info in the background and then calling notifyDataSetChanged() on the main thread. I could load all the info in the background and then assign the info and update on the main thread. – Joel Page Apr 17 '19 at 00:26
1

i had a similar issue. i had 5 devices and i was getting an error only in one device (lenovo - k6, nougat 7.0). It was running in other nougat phones. It was a device specific error

On navigation from Activity A > Activity B, onDestroy() of activity A was getting called . in onDestroy() i was clearing my static arraylist.[ It happened only in lenovo - k6, nougat 7.0]

I was getting error OnScrolling on recyclerView in activity B due to empty list i was notifying to adapter.

Hope it helps someone.

1

The issue may be related to the adapter. If you are using setHasStableIds(true); in the adapter constructor, you need to make sure that you are using stable ids.

Make sure that you override getItemId correctly in the adapter. It should be:

@Override
public long getItemId(int position) {
   return yourList == null ? 0 : yourList.get(position).getId();
}

And not:

@Override
public long getItemId(int position) {
     return position;
}
Nermeen
  • 15,883
  • 5
  • 59
  • 72
1

I had this error because I forgat to remove this line of code, when setting recyclerview and adapter:

mRecyclerView.setHasFixedSize(true);

You can't have fixed size and remove or add or have filterable recycleview (with search). Check for that line in your code.

0

This Problem is mostly caused by position issue .

if u remove a postion or make any change call notifydatasetchanged then only the change will takes place.

simply if your remove a postion or clear a array if notydatasetchanged is not called the recyclerview will keep the postions init.

if there is an array of data, i just cleared the array and try to scroll recyclerview this issue will be shown because notydatasetchanged not called . (recycler view has postion but array no positon is found)

NB: sorry for my bad english

Nadil
  • 56
  • 7
  • I am sorry but I already know notifyDatasetChnaged is used to update recyclerview. and i already did it. – Moinkhan Jun 26 '18 at 06:17
0

@Moinkhan try to add holder.setIsRecyclable(false) in onBindViewHolder

Dev
  • 181
  • 12
0

Use this method in the adapter class:

@Override
public long getItemId(int position) {
    return (null != feedItemList ? feedItemList.size() : 0);
}
Dada
  • 6,313
  • 7
  • 24
  • 43