IndexOutOfBoundsException 在重新填充 RecyclerView 后使用 AsyncTask 和



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

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

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

我的异步任务doInBackground()方法:

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

和我的异步任务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();
}
}

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

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

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

@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因为它在Android 11上已弃用

最新更新