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)
...
}
我有一个来自 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)
...
}