使用 AsyncTask 和 notifyItemInserted() 重新填充 RecyclerView 后出现 IndexOutOfBoundsException

IndexOutOfBoundsException after refilling RecyclerView with AsyncTask and notifyItemInserted()

我有一个使用 AsyncTask 填充数据的 RecyclerView。当我用 clear()mAdapter.notifyDataSetChanged() 清除列表然后尝试用 AsyncTask 再次填充它时,我得到这个异常:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter position

在我的 AsyncTask 中用 notifyDataSetChanged() 替换 notifyItemInserted() 解决了问题,但我认为这不是一个好的解决方案,我想了解为什么第一种方法不起作用.

我的 AsyncTask doInBackground() 方法:

@Override
        protected Void doInBackground(Void... voids) {
            for (int i = 0; i < 100; i++) {
                mDataContainer.addItem(i);
                publishProgress(i);
            }
            return null;
        }

和我的 AsyncTask onProgressUpdate() 方法:

@Override
        protected void onProgressUpdate(Integer... values) {
            mAdapter.notifyItemInserted(values[0]);
            super.onProgressUpdate(values);
        }

我希望有人能帮助我解决这个问题。提前致谢。


编辑: 这是适配器:

private class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
        private List<Item> mItems;
        private int selectedPos = RecyclerView.NO_POSITION;

        private class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
            private Item mItem;
            private TextView mNameTextView;
            private TextView mMembersTextView;

            MyViewHolder(View view) {
                super(view);
                itemView.setOnClickListener(this);
                mNameTextView = itemView.findViewById(R.id.item_name);
                mMembersTextView = itemView.findViewById(R.id.item_members);
            }

            @Override
            public void onClick(View view) {
                notifyItemChanged(selectedPos);
                selectedPos = getLayoutPosition();
                notifyItemChanged(selectedPos);
                mOnItemSelectedListener.onItemSelected(mItem);
            }
        }

        MyAdapter(List<Item> items) {
            mItems = items;
        }

        @NonNull
        @Override
        public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(getActivity())
                    .inflate(R.layout.list_item, parent, false);

            return new MyViewHolder(view);
        }

        @Override
        public void onBindViewHolder(MyViewHolder myViewHolder, int position) {
            Item item = mItems.get(position);
            myViewHolder.mItem = item;
            myView.mNameTextView.setText(item.getName());
            myView.mMembersTextView.setText(String.format(Locale.US,"%d/50", item.getMembers()));

            if (!mIsInit) {
                // select item that was selected before orientation change
                if (selectedPos != RecyclerView.NO_POSITION) {
                    Item selectedItem = mItems.get(selectedPos);
                    mOnItemSelectedListener.onItemSelected(selectedItem);
                // else select item 0 as default on landscape mode
                } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
                    selectedPos = 0;
                    mOnItemSelectedListener.onItemSelected(MyViewHolder.mItem);
                }
                mIsInit = true;
            }

            if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                myViewHolder.itemView.setSelected(selectedPos == position);
            }
        }

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

我认为最好在后台添加所有值并在所有数据插入后通知适配器

        @Override
        protected Void doInBackground(Void... voids) {
            for (int i = 0; i < 100; i++) {
                mDataContainer.addItem(i);
            }
            return null;
        }

     @Override
     protected void onPostExecute(Long result) {
         mAdapter.notifyItemRangeInserted(0, 100);
     }

我还建议你不要使用 AsyncTask 因为它是 deprecated on Android 11

您应该只从 UI 线程向适配器添加项目(并通知)。从后台更改适配器的支持数据,然后稍后通过 onProgressUpdate 在 UI 线程上通知很可能会导致竞争条件。在这种情况下,应在通知适配器之前将 mDataContainer.addItem(i); 移动到 onProgressUpdate

如果没有看到 mDataContainer 的定义,很难确认这是否是唯一的问题,但在这里修复同步肯定是解决这个问题的第一步。