IndexOutOfBoundsException:滚动时检测到不一致错误

IndexOutOfBoundsException: Inconsistency detected error while scrolling

我有一个来自 API 的列表,我存储在数据库中。现在我正在从数据库中获取列表并将其显示在 recyclerview 中。然后从 recyclerview 列表中逐一删除所有数据。使用 JobScheduler 每 10 秒重复一次此过程。当我滚动时,出现此错误。我已经尝试了各种 SO 帖子中给出的许多解决方案,例如 但它没有用。

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionMessageViewHolder{c2276c4 position=31 id=-1, oldPos=-1, pLpos:-1 no parent} androidx.recyclerview.widget.RecyclerView{5223262 VFED..... .F...... 0,0-720,1120 #7f0900d4 app:id/recycler}, adapter:com.sam.testapp.MessageAdapter@4b57ff3, layout:androidx.recyclerview.widget.LinearLayoutManager@735b4b0, context:com.sam.testapp.MainActivity@e780e56
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MessageViewHolder> {

    private List<Message> messageList;

    public MessageAdapter(List<Message> messageList) {
        this.messageList = messageList;
    }

    protected static class MessageViewHolder extends RecyclerView.ViewHolder {
        private ConstraintLayout root;
        private TextView umfiTXT, msgTXT;

        MessageViewHolder(View view) {
            super(view);
            root = view.findViewById(R.id.root);
            umfiTXT = view.findViewById(R.id.umfiTXT);
            msgTXT = view.findViewById(R.id.msgTXT);
        }
    }

    @NonNull
    @Override
    public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new MessageViewHolder((LayoutInflater.from(parent.getContext())).inflate(R.layout.row_msg_list, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull MessageViewHolder holder, int position) {
        try {
            holder.umfiTXT.setText("UMFI: " + messageList.get(position).getMessageUMFI());
            holder.msgTXT.setText("Message: " + messageList.get(position).getMessageTxt());
        }catch(Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public int getItemCount() {
        try {
            if(messageList.isEmpty())
                return 0;
            else
                return messageList.size();
        }catch(Exception e) {
            return 0;
        }
    }

    private void clearData() {
        this.messageList.clear();
        notifyDataSetChanged();
    }

    public void setData(List<Message> data) {
        clearData();
        this.messageList.addAll(data);
        notifyDataSetChanged();
    }
}
public class MainActivity extends AppCompatActivity {

    private ArrayList<Message> messageList;
    private RecyclerView recycler;
    private AppCompatTextView emptyTxt;
    private MessageAdapter messageAdapter;
    private SqliteDatabase database;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        messageList = new ArrayList<>();
        recycler = findViewById(R.id.recycler);
        emptyTxt = findViewById(R.id.emptyTxt);
        database = new SqliteDatabase(MainActivity.this);


        recycler.setLayoutManager(new LinearLayoutManager(this));
        messageAdapter = new MessageAdapter(messageList);
        recycler.setAdapter(messageAdapter);
        recycler.setItemAnimator(null);

        RxBus.subscribe((Consumer<Object>) o -> {
            if (o instanceof RxEvent) {
                RxEvent data = (RxEvent) o;
                System.out.println("SAM: status: " + data.getStatus());
                if (data.getStatus() == 1) {
                    fetchMessageList();
                }

            }
        });


    }

    private void fetchMessageList(){
        messageList.clear();
        AndroidNetworking.get(Util.url)
                .setPriority(Priority.IMMEDIATE)
                .build()
                .getAsJSONArray(new JSONArrayRequestListener() {
                    @Override
                    public void onResponse(JSONArray jsonArray) {
                        try{
                            System.out.println("SAM: fetchMessageList jsonArray: "+jsonArray);
                            ArrayList<Message> templist = new ArrayList<>();
                            for(int i=0; i<jsonArray.length(); i++){
                                JSONObject jsonObject = jsonArray.getJSONObject(i);
                                templist.add(new Message(jsonObject.getString("umfi"), jsonObject.getString("msg_to"), jsonObject.getString("msg_text")));
                            }

                            storeListinDB(templist);

                        }catch(Exception e){
                            e.printStackTrace();
                        }

                    }
                    @Override
                    public void onError(ANError error) {
                        System.out.println("SAM: fetchMessageList onError: "+error.getErrorBody());
                    }
                });
    }

    private void storeListinDB(ArrayList<Message> templist){
        database.insertArrayData(templist);
        showList();
    }

    private void showList(){
        try{
            //recycler.getRecycledViewPool().clear();


            if(messageList.size()>0){
                emptyTxt.setVisibility(View.GONE);
                messageAdapter.notifyDataSetChanged();
            }else{
                emptyTxt.setVisibility(View.VISIBLE);
                ArrayList<Message> templist = new ArrayList<>();
                templist = database.fetchMessageList();
                messageAdapter.setData(templist);
                System.out.println("SAM: templist size: "+templist.size());
            }
            System.out.println("SAM: messageList size: "+messageList.size());
            //removeAll();
        }catch(Exception e){
            e.printStackTrace();
        }
    }


}

您将 messageList 传递给适配器,因此适配器中的 messageList 和 activity 是相同的对象引用。然后,据我了解,以某种方式调用了 fetchMessageList 方法,这就是问题出现的地方。您在列表上调用 .clear() 并开始异步操作以获取新列表,然后将其同步 post 到您的适配器。

问题是,在您清除列表后,您的适配器现在保留对空列表的引用,而不会收到有关更改的通知。适配器仍然“认为”您的列表与之前的大小相同,因此当您滚动 RecyclerView 时,它会尝试至少调用 onBindViewHolder 以获取新出现的项目。但是,由于列表是空的,它会抛出 IndexOutOfBoundsException.

您可以尝试在调用 messageList.clear() 后立即通知适配器有关更改,但在我看来,只需删除此清除即可解决问题。

private void fetchMessageList(){
        //messageList.clear(); <- delete this
        AndroidNetworking.get(Util.url)
            ...
}