RecyclerView触发IndexOutOfBoundsException异常排查求助
咱们先来梳理下你遇到的问题:
选择Toolbar上的「指定日期」筛选选项,弹出日期选择器后,RecyclerView没有数据填充,触摸屏幕直接崩溃,抛出
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder异常;但升序、降序筛选时一切正常。
这个异常本质是RecyclerView的数据源和Adapter的状态不一致,结合你的场景和代码,我整理了针对性的修复方案:
一、核心问题分析
- 数据源更新不规范:日期筛选后请求新数据时,可能没有清空旧数据,导致新旧数据混杂,Adapter无法正确识别有效position;
- Adapter通知时机错误:每次添加单条数据就调用
notifyDataSetChanged(),容易引发状态不一致; - ViewHolder池操作不当:调用
mMessagesView.getRecycledViewPool().clear()可能破坏RecyclerView的ViewHolder管理逻辑; - 空数据未处理:日期筛选后如果后端返回空数据,RecyclerView触摸时会尝试访问无效的position。
二、针对性修复步骤
1. 规范数据源更新逻辑
在接收新的订单数据前,先清空旧数据源,避免新旧数据冲突。修改你的orderCompletedData监听逻辑:
private Emitter.Listener orderCompletedData = new Emitter.Listener() { @Override public void call(final Object... args) { if(getActivity() != null) { getActivity().runOnUiThread(() -> { JSONArray object1 = (JSONArray) args[0]; if(object1 != null){ JSONArray array = object1.optJSONArray(0); // 第一步:清空旧数据,保证数据源纯净 mMessages.clear(); int arraylength = array.length(); for(int j=0; j<array.length();j++){ JSONObject item = array.optJSONObject(j); // 补全你的数据解析逻辑(这里假设你能从item中取出对应字段) String orderId = item.optString("order_id"); String order = item.optString("order"); String currencyname = item.optString("currencyname"); String amount = item.optString("amount"); String filledAmount = item.optString("filledAmount"); String proceeds = item.optString("proceeds"); String orderPrice = item.optString("orderPrice"); String orderDate = item.optString("orderDate"); String orderType = item.optString("orderType"); // 添加单条数据到数据源 addMessage(orderId, order, currencyname, amount, filledAmount, proceeds, orderPrice, orderDate, orderType); } // 统一通知Adapter更新,避免重复通知 mAdapter.notifyDataSetChanged(); } else { // 如果后端返回空数据,清空数据源并通知 mMessages.clear(); mAdapter.notifyDataSetChanged(); } }); } } };
同时修改addMessage方法,移除里面的notifyDataSetChanged():
private void addMessage(String orderId,String order,String currencyname,String amount,String filledAmount,String proceeds,String orderPrice,String orderDate,String orderType) { mMessages.add(new PendingOrderModel.Builder(PendingOrderModel.RECEIVER) .orderid(orderId).order(order).amount(amount).currencyname(currencyname) .filledamount(filledAmount).orderprice(orderPrice).proceeds(proceeds) .orderdate(orderDate).ordertype(orderType).build()); // 这里不再单独通知,统一在循环结束后调用notifyDataSetChanged() }
2. 日期筛选后重置数据源
当用户选择完日期后,先清空旧数据再请求新数据,避免旧数据残留:
// 示例:日期选择完成后的回调方法(你需要根据自己的日期选择器逻辑调整) private void onDateConfirmed(String selectedDate) { filterDateString = selectedDate; filterString = "date_specified"; // 记得设置对应的筛选类型,和后端约定好 // 先清空旧数据并通知Adapter mMessages.clear(); mAdapter.notifyDataSetChanged(); // 重新请求筛选后的数据 mSocket.emit("fetch_pending_orders", getSendMessageString("")); }
3. 修复Adapter实例不一致问题
你的代码在onAttach和onViewCreated中都初始化了mAdapter,这会导致多个Adapter实例共存,状态混乱。建议只在onViewCreated中初始化:
@Override public void onAttach(Context context) { super.onAttach(context); // 移除这里的mAdapter初始化代码,统一在onViewCreated中处理 if (context instanceof Activity) { } }
4. 移除不当的RecycledViewPool操作
删除addMessage中的mMessagesView.getRecycledViewPool().clear();,这个操作会干扰RecyclerView的ViewHolder复用机制,容易引发状态异常。
5. 在Adapter中处理空数据
修改PendingOrderAdapter,添加空数据和无效position的判断,避免触摸空列表崩溃:
@Override public int getItemCount() { // 确保数据源不为空时返回长度,否则返回0 return mMessages != null ? mMessages.size() : 0; } @Override public void onBindViewHolder(@NonNull PendingOrderViewHolder holder, int position) { // 校验数据源和position的有效性 if (mMessages == null || position >= mMessages.size()) { holder.itemView.setVisibility(View.GONE); return; } // 正常绑定数据的逻辑 PendingOrderModel model = mMessages.get(position); // ... 绑定数据到ViewHolder }
6. 优化Fragment生命周期管理
在Fragment销毁时解绑Socket监听,避免内存泄漏和无效回调:
@Override public void onDestroy() { super.onDestroy(); if (mSocket != null) { mSocket.off("pending_orders_data", orderCompletedData); mSocket.disconnect(); } }
三、额外检查点
- 确认日期筛选时
filterString和filterDateString的参数正确,和后端约定一致,避免后端返回错误或空数据; - 确保所有修改数据源和通知Adapter的操作都在UI线程执行(你的代码已经用
runOnUiThread,这点保持即可)。
内容的提问来源于stack exchange,提问作者Himanshu Agnihotri

