본문 바로가기
안드로이드

안드로이드 ListView와 Thread를 사용할 때 동기화 오류 (IllegalStateException)

by 호군 2012. 10. 8.
반응형


버그

안드로이드 App을 개발하면, ListView나 Gallery 등의 위젯들을 사용하게 됩니다. 그리고 속도 향상을 위해서 Thread나 AsyncTask와 같은 별도의 Thread를 만들어서 처리를 합니다. 운이 좋으면 발견 되지 않겠지만(과연 좋은 걸까라는 생각이 들지만..) , 타이밍이 맞아서 App이 죽는 경우가 발생하기도 합니다. 이렇게 App이 죽는 이유는 동기화 입니다.  아래 Log를 발견했다면, 아마 이 이유라고 생각됩니다.


E/AndroidRuntime(19101): FATAL EXCEPTION: main

E/AndroidRuntime(19101): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notifi

cation. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131

296370, class android.widget.ListView) with Adapter(class com.dhna.apps.MyActivity$MyListAdapter)]

E/AndroidRuntime(19101):        at android.widget.ListView.layoutChildren(ListView.java:1492)

E/AndroidRuntime(19101):        at android.widget.AbsListView.onLayout(AbsListView.java:1147)

E/AndroidRuntime(19101):        at android.view.View.layout(View.java:7077)

E/AndroidRuntime(19101):        at android.widget.FrameLayout.onLayout(FrameLayout.java:333)

E/AndroidRuntime(19101):        at android.view.View.layout(View.java:7077)

E/AndroidRuntime(19101):        at android.widget.FrameLayout.onLayout(FrameLayout.java:333)

E/AndroidRuntime(19101):        at android.view.View.layout(View.java:7077)

E/AndroidRuntime(19101):        at android.widget.FrameLayout.onLayout(FrameLayout.java:333)

E/AndroidRuntime(19101):        at android.view.View.layout(View.java:7077)

E/AndroidRuntime(19101):        at android.widget.FrameLayout.onLayout(FrameLayout.java:333)

E/AndroidRuntime(19101):        at android.view.View.layout(View.java:7077)

E/AndroidRuntime(19101):        at android.view.ViewRoot.performTraversals(ViewRoot.java:1045)

E/AndroidRuntime(19101):        at android.view.ViewRoot.handleMessage(ViewRoot.java:1727)

E/AndroidRuntime(19101):        at android.os.Handler.dispatchMessage(Handler.java:99)

E/AndroidRuntime(19101):        at android.os.Looper.loop(Looper.java:123)

E/AndroidRuntime(19101):        at android.app.ActivityThread.main(ActivityThread.java:4627)

E/AndroidRuntime(19101):        at java.lang.reflect.Method.invokeNative(Native Method)

E/AndroidRuntime(19101):        at java.lang.reflect.Method.invoke(Method.java:521)

E/AndroidRuntime(19101):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)

E/AndroidRuntime(19101):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)

E/AndroidRuntime(19101):        at dalvik.system.NativeStart.main(Native Method)

W/ActivityManager(  717):   Force finishing activity com.dhna.apps/.MyActivity




버그 해결 방법

Log에 아래와 같은 내용이 있습니다. Log님께서 말하시는데로 Adapter의 데이터를 UI쓰레드 이외의 쓰레드에서 변경하는 부분이 있는지 살펴봐야겠습니다.

"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."


살펴보면, UI쓰레드에서 BaseAdapter.notifyDataSetChanged() 메소드를 호출하지만, UI쓰레드 이외에서 Adapter의 데이터를 변경 하고 있을 가능성이 있습니다.


그렇다면 BaseAdapter.notifyDataSetChanged()를 호출하는 곳과 데이터를 추가하는 곳에서 동기화를 맞추면 됩니다. 보통 동기화는 syncronized 키워드를 사용해서 할 수 있지만, 메소드를 override하지 않는 이상 데이터를 그리는 타이밍을 완벽히 제어 할 수가 없습니다.

그래서 쉽게 할 수 있는 방법은 '데이터 갱신'과 '화면 그리기'를 같은 쓰레드에서 작업 하도록 하는 것 입니다. '화면 그리기' 작업은 Main쓰레드에서 하기 때문에 '데이터 갱신' 역시 Main쓰레드에서 하도록 만들면 됩니다. Main쓰레드에서 작업하도록 하는 방법은 Handler와 Activity.runOnUiThread()를 이용하는 방법이 있습니다. 








반응형