본문

안드로이드의 Handler #1. 설명 with Looper+MessageQueue

Handler 를 new 하게 되면 현재 new 를 실행한 thread 에 bind 되게 된다. 그래서 이 Handler 는 현재 thread 의 message queue 를 이용하게 되고, Handler 의 method 인 post() 나 sendMessage() 를 이용하면, 현재의 message queue 에 Runnable 또는 Message 가 들어가게 된다. 이렇게 message queue 에 저장된 녀석이 하나씩 처리되는데, 이때 다시 handler 가 Message() 는 handleMessage() 를 통해서 처리하고, Runnable 같은 것들은 정해진 command(run() 같은) 를 호출해서 처리하게 된다. - 출처 : [컴][안드로이드] android Handler 개념 및 사용법


안드로이드 프로세스에는 Activity, Service, ContentProvider, BroadcastReceiver의 4개의 컴포넌트가 존재하며, 대개 이들은 메인스레드라고 부르는 스레드 하나로 처리한다. 컴포넌트 중에서 어떤것을 호출할 때, 그 호출흔 보통 동기 호출이다. 따라서 메인스레드는 이들이 실행되면 실행이 종료될 때까지 기다려야하고, 특히 Activity의 경우, 처리에 시간이 5초이상 소요되는 경우 ANR(Application Not Responding) 메시지를 던진다. 


메인 스레드가 멈추는것을(특히 UI가 멈추는것을) 방지하기 위해, 다른 스레드에서 일거리를 처리하고, 결과만 받아서 메인스레드에서 처리하는 구조가 필요하다. 이것이 핸들러이다. 이 뿐만 아니라 안드로이드 프로세스상에서는 하나의 UI 스레드만이(메인스레드) 동작하여 UI를 관리하기 때문에, 다른 스레드에서 직접 UI를 조작할 수 없다-다른 스레드에서 특정 스레드에 있는 값들을 임의로 변경할 수 없듯(공유위반, 이에 관하여는 '안드로이드에서 Handler 사용? #1'에 소스코드로 설명됨). 따라서, 외부에서의 작동이 그 특정 스레드 상에서 동작하는것과같은 효과를 주는것 또한 핸들러이다.


핸들러의 역할은 다른 스레드와 통신하는 것이다. 핸들러로 메시지 큐에 메시지를 넣으면, 나중에 메인 스레드는 그 메시지를 받아서 처리한다. 메시지를 큐에 넣으면 그 메시지는 내부적으로 큐에 넣은 핸들러를 가리킨다(즉 그 메시지가 어떤 핸들러를 위한 것인지 알 수 있다,). 메시지 인스턴스는 핸들러에 넘길 데이터도 가리킨다. 메인 스레드가 이 메시지를 처리한다는 것은 핸들러 인스턴스의 콜백 메소드를 실행하는 것이다. 이 콜백 메소드의 이름은 handleMessage이다.



사진출처 : http://www.aviyehuda.com/blog/2010/12/20/android-multithreading-in-a-ui-environment/


* MessageQueue와 Looper

메시지는 즉시 호출되는 메서드와는 달리 스레드간의 신호이므로,  보낸다고 해서 바로 바로 처리되는 것은 아니다. 여러 스레드에서 메시지를 동시 다발적으로 보낼 수도 있으므로, 동기적으로 처리할 수 없으며, 어딘가에 쌓아 놓았다가 순서대로 처리해야 한다. 전달되는 메시지를 차곡차곡 쌓아 놓는 것이 바로 메시지 큐(MessageQueue)이다. 핸들러에 추가될 수 있는 메시지나 러너블(Runnable) 객체는 일단 큐에 저장되고, 들어온 순서대로 순차적으로 처리된다. 


큐에서 메시지를 꺼내 핸들러로 전달하는 것은 루퍼(Looper)이다. 루퍼는 무한히 실행되는 메시지 루프를 통해 큐에 메시지가 들어오는지 감시하며 들어온 메시지를 처리할 핸들러를 찾아 handleMessage 메서드를 호출한다. 이 루퍼 역시 동기적으로 수행되기 때문에, 메시지 처리에 시간이 걸리는 경우 해당 메인 스레드에서도 작업이 지연되게 된다. UI를 관리하는 메인 스레드는 기본적으로 루퍼를 가진다. 그래서 별다른 조치가 없어도 핸들러 객체를 만들어 놓기만 하면 메시지를 받을 수 있다. 그러나 계산을 주로 수행하는 작업 스레드는 기본적으로 루퍼를 가지지 않는다. 따라서 이를 위해 해당 스레드 상에서 Looper 클래스의 prepare(), loop(), quit() 메소드를 사용하여 루퍼를 준비(메시지 큐 생성 등의 작업 포함)하고, 루프를 실행시키고, 루프를 종료하는 과정이 필요하다. 


즉, Handler, MessageQueue, Looper는 서로 연결되며, 이때 Handler를 통해 메시지를 입력받아(handler.sendMessage()) MessageQueue에 집어넣고, Looper는 MessageQueue에있는 메시지를 꺼내서, Handler에게 전달하고, 이를 받은 Handler는 자신이 위치한 스레드에 작업을 수행(handler.handleMessage())하는 것이다. Handler(입력) -> MessageQueue -> Looper -> Handler(출력)


원칙상은 위 모두가 구현되고 연결되어야 하지만, Handler에서의 기본생성자(Handler())는 자동으로 자신이 속한 스레드의 MessageQueue와 Looper에 자동으로 연결되기 때문에, 이러한 자세한 사항까지는 알 필요가 없게 되었다. 하지만 역시 MessageQueue와 Looper가 기본으로 갖춰지지 않은 스레드에서 기본생성자를 사용할 경우, 'ERROR/AndroidRuntime(): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()'가 출력될 것이다.


오해하지 말 것은 메시지는 스레드간의 통신 장치일 뿐이라는 점이다. 안드로이드는 시스템 전역적인 메시지 큐를 제공하지 않으므로 메시지를 통해 응용 프로그램끼리 통신할 수는 없다. 특히  윈도우 프로그래머들은 이 점을 주의해야 하는데, 윈도우 환경과 메시지 큐의 구조는 비슷하지만 용도가 완전히 다르다. 안드로이드에서 응용 프로그램간의 공식적인 통신 장치는 인텐트와 브로드캐스트 리시버이다.



사진출처 : http://www.vineetgupta.com/2011/03/mobile-platforms-part-1-android/



참고 : 안드로이드 프로그래밍 정복

안드로이드 4 실무 바이블

안드로이드 Handler 이야기 - Handler 와 Looper

[android/안드로이드] Handler 의 내부적 진실.



???


댓글

Holic Spirit :: Tistory Edition

design by tokiidesu. powerd by kakao.