ListView에서 java.lang.IllegalStateException 에러 처리

서비스 하는 앱 중의 하나가 크래시를 리포팅해서 확인해보니, 다음과 같다. 이 앱에서 리포팅한 에러는 아래와 같다. 그리고 이 에러에서 보듯이 리스트뷰(ListView)를 사용하고 있다.

12-12 13:27:04.662: E/AndroidRuntime(15705): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131230758, class android.widget.ListView) with Adapter(class com.xxx.xxx.XXXAdapter)]

구글님께 문의를 한 결과, 화면에 리스트뷰가 보이는 상황에서, 데이터를 추가하는 경우에 발생할 수 있고 아래와 같이 해결하라고 한다.

listview.add(item);
 adapter.notifyDataSetChanged();

or

adapter.add(item)

하지만 같은 에러가 계속 발생했다. 그래서 에러를 트레이스 해보니, 리스트뷰에 있는 layoutChildren() 메서드(리시트뷰 노드가 가지고 있는 하위 트리를 다시 그리는)에서 예외를 발생시킨다. 이 소스에서 예외를 발생시키는 부분을 확인해 보니 다음과 같다.

// Handle the empty set by removing all views that are visible
 // and calling it a day
 if (mItemCount == 0) {
 resetList();
 invokeOnItemScrollListener();
 return;
 } else if (mItemCount != mAdapter.getCount()) {
 throw new IllegalStateException("The content of the adapter has changed but "
 + "ListView did not receive a notification. Make sure the content of "
 + "your adapter is not modified from a background thread, but only from "
 + "the UI thread. Make sure your adapter calls notifyDataSetChanged() "
 + "when its content changes. [in ListView(" + getId() + ", " + getClass()
 + ") with Adapter(" + mAdapter.getClass() + ")]");
 }

이 소스로 mItemCount와 mAdapter.getCount()가 다르면 예외를 발생시키는 것을 알 수 있다. 즉, 위 구글님의 검색결과의 해결책은 맞는 답이다. 그런데 내 경우에는 계속 문제가 발생했다. 디버깅을 해보니, Adapter의 getCount() 메서드가 넘겨주는 값이 다른 값을 넘겨주는 경우가 발생했고 그래서 이 에러가 발생하는 것이었다. 리스트뷰의 스크롤과 클릭 이벤트를 리스너로 등록해서 이벤트가 발생하는 경우에 UI를 처리하기 위해서 UI 스레드로 처리하는 부분들이 있는데 이 영향으로 그런 것도 같다(정확하게 디버깅을 하지 않아서 추측임).

그래서, 내가 사용하는 리스트뷰는 동적으로 데이터를 입력하거나 삭제하지 않는 경우라서, 위 소스를 기준으로 Adapter클래스에서 데이터의 개수를 미리 정해놓고, getCount() 메서드는 정해진 값을 넘겨주도록 했다. 아래가 수정한 소스이다.

public XXXAdapter(Context ctx, int resource, List<Item> items) {
  super(ctx, resource, items);
  this.ctx = ctx;
  this.count = items.size();
  this.inflater = LayoutInflater.from(ctx);
}

@Override
public int getCount() {
  return count;
}

이 소스로 수정한 뒤에 이 에러는 발생하지 않는다.

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.