Android에서 UI 작업은 별도의 스레드가 아닌 메인 스레드에서 작업해야 한다.
로직상, 다른 스레드에서 UI 처리를 해야 한다면 해당 스레드와 메인 스레드를 연결해주는 Handler를 사용해야한다.
오늘은 그 handler에 대해 자세히 정리하고자 한다.
UI작업을 비동기적으로 처리한다면 반드시 동기화 문제에 마주치게 된다.
안드로이드는 이런 문제를 막기위해 병렬 동작하는 Main Thread와 Working Thread 사이에 Handler를 두고 UI 작업은 모두 Main Thread로 전달하게 하였다.
Main Thread에서 UI 작업이 이루어진다고 하였다.
그렇기에, 시간이 오래 걸리는 작업을 메인 스레드에서 하게 되면 UI 처리가 늦어진다.
느린 어플을 좋아할 사람은 아무도 없을 것이다.
다른 스레드에게 메세지를 전달하려면 수신 대상 스레드에서 생성한 핸들러의 post나 sendMessage등의 함수를 사용해야 한다. 그래야 수신 대상 스레드의 Message Queue에 message가 저장되기 때문에.
Message Queue에 저장된 message나 runnable은 Looper가 차례로 꺼내서 핸들러로 전달한다.
결국, Handler가 넣어 놓은 것을 다시 빼서 실행 시키는 격이다.
위에서 말한대로.
Handler1이 메인 스레드라면, Handler2, Handler3는 또 다른 작업 스레드이다.
Handler2 or Handler3에서 UI작업을 요청하면 Message Queue에 쌓이고 이를 Looper가 실제 UI작업을 실행하는 handlerMessage를 통해 MainThread에서 순차적으로 실행하게 하는 것이다.
그렇기에 Handler의 생성자에 Looper가 전달되는데 이는 모든 Handler가 동일한 Looper를 이용하게 된다.
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
// something..
}
}, 1000);
위에서 말한대로 Handler 생성자에 Looper를 넣게 된다.
위의 예제는 Delay를 주는 코드이다.
Handler.sendMessage(Message msg)
: Message 객체를 message queue에 전달하는 역할
Handler.sendEmptyMessage(int wht)
: Message의 wht 필드를 전달하는 함수
post()
: post()는 Message 객체가 아닌 Runnable 객체를 Message queue에 전달한다.
ex) Handler.post(new Runnable()) // 위에서 본 코드.
new Thread(new Runnable(){
@Override
public void run() {
WHandler whandler;
whandler = new WHandler();
whandler.post(new Runnable(){
@Override
public void run() {
//여기서의 UI 작업은 안된다.
}
}
}
}
당연히 안된다. UI작업은 메인스레드에서만 가능하기에.
하고싶다면 메인 스레드에서 해야 한다.
new Thread(new Runnable(){
@Override
public void run() {
// 여기서는 UI 작업 불가.
mainHandler.post(new Runnable(){
@Override
public void run() {
//여기서의 UI 작업은 가능.
}
}
}
}
mainHandler로 UI작업을 진행한다.
워크 스레드 내부에서 메인 스레드의 핸들러를 통해 Runnable 객체를 전달한다.
그래서 메인 스레드의 Message Queue에 Runnable 객체가 저장된다.
메인 스레드의 Looper가 Runnable 객체를 꺼내서 run()을 실행한다.
MainThread
Handler mainHandler = new Handler() 이르캐 생성해주고.
Work Thread
mainHandler.post(new Runnable()) 이르캐 불러서 실행해주면
Main Thread의 mainHandler를 통해 run()을 실행하는 격이다.