버그
안드로이드 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()를 이용하는 방법이 있습니다.
'안드로이드' 카테고리의 다른 글
[안드로이드] ActionBar Indicator 커스텀마이징하기 (0) | 2013.10.30 |
---|---|
[안드로이드] InputStream을 닫는데 왜 오래 걸릴까? (0) | 2013.04.19 |
[안드로이드] Dialog.dismiss()를 할 때, java.lang.IllegalArgumentException: View not attached to window manager 예외 회피하기 (2) | 2013.03.08 |
[안드로이드] Collection을 사용 할 때 ConcurrentModificationException을 피하는 방법 (0) | 2013.03.06 |
[안드로이드] APK 파일 SIGN 하기 (0) | 2012.10.29 |
[안드로이드] uncaught exception 사용하기 (UncaughtExceptionHandler) (0) | 2012.10.02 |
[안드로이드] Context를 Activity로 강제 타입 캐스팅시 발생하는 문제(Shutting down VM) (0) | 2012.10.02 |
[안드로이드] ListView의 fadingEdge 없애는 방법 (0) | 2012.09.26 |
[안드로이드] /system 디렉토리 remount 하기 (0) | 2012.09.19 |
[안드로이드] 텍스트 파일의 캐릭터 셋(Character Set) 얻기 (0) | 2012.09.04 |