RecyclerView异常BUG

本文记录了一个关于RecyclerView出现IndexOutOfBoundsException的问题及解决过程。问题出现在数据更新时,由于Adapter内部数据与外部数据不一致导致异常。通过调整数据同步方式解决了该问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                                                              05-05 11:47:52.021 4882-4882/com.daijintao.youjin E/AndroidRuntime: FATAL EXCEPTION: main
                                                                    Process: com.daijintao.youjin, PID: 4882
                                                                    java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{184736a1 position=1 id=-1, oldPos=1, pLpos:-1 scrap [attachedScrap] tmpDetached no parent}                                                                   
at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:5297)
        at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5479)
        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5440)
        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5436)
        at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2224)
        at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1551)
        at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1511)
        at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:595)
        at android.support.v7.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:3534)
        at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:3019)
        at android.view.View.measure(View.java:17547)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1436)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:722)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:613)
        at android.view.View.measure(View.java:17547)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1436)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:722)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:613)
        at android.view.View.measure(View.java:17547)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1436)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:722)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:613)
        at android.view.View.measure(View.java:17547)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
        at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:139)
        at android.view.View.measure(View.java:17547)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1436)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:722)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:613)
        at android.view.View.measure(View.java:17547)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
        at android.view.View.measure(View.java:17547)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1436)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:722)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:613)
        at android.view.View.measure(View.java:17547)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2615)
        at android.view.View.measure(View.java:17547)
        at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2015)
        at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1173)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1379)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1061)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5885)
        at android.view.Choreographer$Callb

这么晚了还在调试BUG实在伤不起,不过作为一个准程序员来说这点幸苦当然算不了什么。现在把今天碰到的一个比较令人不解的错误记录下来,以备日后随时翻查。
这么长一串异常信息实在很令人头疼,因为异常的地方根本没有跟自己的代码无关,所以在看了几个错误之后我放弃了,果断度娘。在几经搜索之后,终于在一篇博文中搜到了结果,这里我就引用下网上大神的原话:

文章中说到:在进行数据移除和数据增加时,务必要保证RVAdapter中的数据和移除的数据保持一致!什么意思呢?我自己琢磨认为是这样的,如果你更新你的集合后,调用RVAdapter的新出现的notifyxxxx方法时,adapter 的更新预期结果和实际集合更新结果不同,那么就会出现异常了。
举个例子吧: 比如你集合remove了两条数据,但是你RVAdapter只notifyItemRemoved(2)(这里表示移除第三条数据,只移除了一条),这种情况旧属于数据不一致了。还有一种是你增加数据,你集合增加10条数据,但是你RVAdapter的notify只增加了5条数据,这也是数据不一致。
好了,通过以上两种情况,大概就知道了,这个数据一致其实说的是要保证数量一致。就是说RVAdapter有个size,你的集合有个size。这两个size,在调用RvAdapter的notifyxxxx时候必须保持相同。

看到这里,似乎茅塞顿开,去翻看自己的代码,果然在一个不起眼的位置发现了异常。

editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }
            @Override
            public void afterTextChanged(Editable s) {
                if (s.toString().equals("")) {
                    ryHint.setVisibility(View.GONE);
                    searchList.clear();
                    hintAdapter.notifyItemChanged(0);
                }else {
                    //当内容清空的时候恢复初始值
                    loadYoujinData(s.toString());
                }
            }
     });

当我们清空输入内容的时候会执行if语句的第一个判断,这里的hintAdapter调用了notifyItemChanged(0)这个方法,然后是loadYoujiData这个方法的内容,这个方法是根据输入内容去请求网络数据,以下是这个方法里的关键部分,这里我使用了Bmob后端云作为我的云端服务器。

youjinBmobQuery.findObjects(new FindListener<Youjin>() {
            @Override
            public void done(List<Youjin> list, BmobException e) {
                if (e == null) {
                    if (list.size() == 0) {
                        //返回数据大小为0
                        Toast.makeText(MainActivity.this, "数据不存在", Toast.LENGTH_SHORT).show();
                    } else {
                        ryHint.setVisibility(View.VISIBLE);
                        //设置RecyclerView
                        searchList.clear();
                        searchList.addAll(list);
                        for(Youjin youjin:searchList){
                            Log.i("TAG",youjin.getName());
                        }
                        hintAdapter.notifyDataSetChanged();
                    }
                } else {
                    Toast.makeText(MainActivity.this, "加载错误", Toast.LENGTH_SHORT).show();
                    Log.i("TAG", "加载错误:" + e.getMessage().toString());
                }

可以看到这里的adapter使用了notifyDataSetChaned();
这样写导致的结果就是两次notifyXXX时所更新的数据和实际不一致,当我把两个调用notifyXXX的地方对调notifyXXX方法后发现错误仍然存在,而两处调用同一个notify方法,异常却没有了。

以上只是我个人的看法,欢迎大家多多指点,指出错误的地方,让小弟我及时查漏补缺。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值