이번 과제에서 안드로이드 클라이언트 파트를 만드는 도중 진저브레드인 폰에서는 제대로 동작하는 코드가 갤럭시 탭 10.1에서는 동작하지 않아서 삽질을 좀 하게 되었습니다. 구글링한 결과 해당 예외의 원인은 "안드로이드 OS 3.0 버전부터 Main Thread에서 네트워크 관련 작업을 할 수 없게 되었다" 였습니다.
Main Thread에서는 UI 관련 작업을 해야 하는데 네트워크 작업은 시간이 오래 걸릴지도 모르는 작업인데 그동안 UI 관련 작업을 처리해 줄 수 없어 3.0 버전부터는 아예 Main Thread에서 네트워크 작업을 할 수 없게 강제화시켰다는 내용이었습니다.
제 코드는 Thread에서 통신을 하고 Handler에서 이 Thread들을 제어하는 구조에, Handler는 Main Thread에서 생성해서 사용하는 방식이었습니다. 여기서 문제점은 Handler는 자신을 생성한 스레드에 의존적이라는 점이었는데, 저는 이걸 모르고 코드를 작성해서 문제가 발생한 것이었습니다.
알아본 해결법들로는 3가지가 있습니다.
- 먼저, Handler와 Thread 대신 AsyncTask를 사용하면 해결됩니다. AsyncTask가 나오게 된 배경도 Handler와 Thread로 나누어진 코드를 보다 간단히 캡슐화할 수 있고 되어 있었지만, 코드를 다시 작성해야 해서 이 방법말고 다른 방법이 없을까 하고 더 찾아보았습니다.
- 다음으로, StrictMode가 있는데 이 방법을 사용하면 기존 코드의 변경없이 간단한 코드 한줄 추가로 해결이 되지만, 개발 버전에서만 사용하고 배포할 때는 빼기를 추천하고 있었습니다. 이걸 그대로 쓰기는 찜찜하고, AsyncTask를 사용하자니 작업량이 많고.. Handler와 Thread를 그대로 쓸 수 있는 방식을 더 찾다가 간단한 방법을 찾았습니다.
- Handler를 생성, 관리하는 Thread를 만드는 것이 제가 선택한 방법입니다. Main Thread에서 바로 Handler를 생성하지 않고, Handler를 생성할 Thread를 생성해주면 됩니다. 다만, Handler를 생성하려면 해당 Thread가 Handler 생성을 지원할 수 있어야 하는데 아래와 같은 방식으로 해결할 수 있었습니다.
// 핸들러를 생성할 스레드의 run() 메소드 public void run() { Looper.prepare(); /* * 여기에서 핸들러 생성 */ Looper.loop(); }
이렇게 하고, 핸들러를 생성한 스레드에서 메소드를 따로 만들어 해당 핸들러로 message를 날려주는 방식으로 작성했습니다.
+ 2012.11.05 추가사항
Looper.loop() 호출 부분에서 블록됩니다.
위 코드에서 루프를 걸고 socket을 read하고 있을 때 Looper.loop()를 호출하면 더 이상 다음 번 루프로 넘어가지 않았습니다. 그래서 더미 스레드를 하나 만들고 그 안에 위 코드를 집어 넣는 걸로 급하게 해결은 했지만, 다음 번에는 처음부터 AsyncTask로 만들어야겠습니다.
한 달 전 쯤에 발견했는데 까먹고 있다가 이제서야 올리네요.
* 출처
http://kimeunseok.com/archives/853
http://blog.naver.com/PostView.nhn?blogId=rgjoon&logNo=90094338577
'스터디 > 안드로이드' 카테고리의 다른 글
NDK :: android-ndk-r8c에서 언제나 rebuild되는 문제 (0) | 2013.01.06 |
---|