用AsyncTask和notifyItemInserted()重新填充RecyclerView之后的IndexOutOfBoundsException

时间:2019-12-13 01:07:53

标签: android android-recyclerview android-asynctask indexoutofboundsexception

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

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

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

我的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();
        }
    }

2 个答案:

答案 0 :(得分:1)

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

        @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

答案 1 :(得分:1)

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

如果不查看mDataContainer的定义,很难确认这是否是唯一的问题,但是在此处解决同步问题绝对是解决此问题的第一步。