티스토리 뷰

Android

Looper, Message, Handler 동작

개발자 요콩 2019.01.06 17:32

Looper

 Looper.prepare() 에서 스레드 별로 Looper를 생성 한다, Looper 생성자에서는 Looper에서 가지는 MessageQueue도 생성한다. 


 생성된 Looper는 ThreadLocal<Looper> sThreadLocal 에 저장된다.  Looper.loop() 를 호출 하면 sThreadLocal 에서 Looper를 꺼내와서 사용한다.


 MainLooper는 ActivityThread 의 main() 함수에서 Looper.prepareMainLooper() 로 생성 하였으므로, Looper.getMainLooper()를 호출 하여 가져올 수 있다.

loop() 메서드 에서는 루프를 도며 MessageQueue 에서 Message를 가져와서 msg.target.dispatchMessage(msg); 를 호출 하여 메시지를 처리한다. 여기서 target은 Handler 이다.

 MessageQueue 에서 다음 Messsage를 가져오는 next() 에서 blocking 상태로 대기하다가 Message가 있으면 return 해준다.

 next() 에서 blocking 상태 이기 때문에 loop() 메서드는 종료 되지 않는다. loop() 메서드를 종료 하기 위해서는 Looper의 quit(), quitSafely() 메서드를 호출 해야 한다. quit(), quitSafely() 메서드가 호출 되면 MesssageQueue에서 next() 로 null 인 Message 를 리턴해 주어 loop() 메서드를 종료 시킨다. 


public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;

    ...

for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
    ...
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...

msg.recycleUnchecked();
}
}


Message

Message 생성 시 Message.obtain(), Handler.obtainMessage 사용을 권장한다. Message 클래스에서는 최대 50개의 Message Pool을 제공하며 obtain() 호출 시 sPool에 저장된 Message를 사용하고 next에 있는 Message를 sPool로 지정한다.

private static Message sPool;
private static int sPoolSize = 0;

private static final int MAX_POOL_SIZE = 50;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}

Looper.loop() 에서 사용된 Message는 recycleUnChecked() 를 호출 하여 Message를 다시 사용한다


Handler


Handler() 기본 생성자는 호출 하는 스레드의 Looper 를 사용하겠다는 의미이다. 메인스레드 에서 생성하는 Handler 는 MainLooper로 UI 작업 할 때 사용된다.

메인스레드가 아닌 백그라운드 스레드 에서 Handler() 기본 생성자 사용시 Exception 이 발생한다. Looper.prepare()로 스레드를 위한 Looper를 생성하고 Looper.loop() 를 호출해 줘야 한다.

백그라운드 스레드에서 메인루프와 연결된 핸들러를 얻기 위해서는 Hander 생성 시 MainLooper를 생성자 파라미터로 넘겨 주면 된다. new Handler(Looper.getMainLooper()) 로 생성된 핸들러에서는 ui 업데이트를 할 수 있다.


Looper.loop() 에서 Handler의 dispatchMessage 메서드를 호출한다. send로 호출할 경우 handleMessage를 호출하고, post 로 호출할 경우 전달된 Runnable 이 Message의 callback 으로 저장된어. callback 을 호출해 준다.

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}


댓글
댓글쓰기 폼