본문 바로가기
프로그래밍/Android

핸들러(Handler), 메시지 큐(MessageQueue), 루퍼(Looper)

by JLearn 2023. 3. 9.

핸들러(Handler)

핸들러는  안드로이드에서 메시지(Message) 처리를 담당하는 클래스입니다. 연결된 스레드의 메시지 큐에 메시지를 보내고, 해당 메시지를 처리합니다.

핸들러는 메시지 큐 뿐만아니라 루퍼와도 관계가 있습니다.

하지만 메시지 큐는 루퍼와 더 관계가 있어 보입니다.

 

메시지 큐(MessageQueue)

메시지 큐는 안드로이드에서 메시지를 보관하는 큐(Queue) 구조체입니다. 메시지 큐는 일반적으로 FIFO(First In First Out) 방식으로 동작하며, 루퍼가 루프를 돌면서 메시지 큐에서 메시지를 가져와 처리합니다.

메시지큐는 핸들러에 의해 사용됩니다. 핸들러는 sendMessage(), post(), postDelayed()와 같은 메소드를 사용하여 메시지를 보내며, 해당 메시지는 메시지 큐에 보관됩니다. Looper가 메시지큐에서 메시지를 가져와서 처리하면 해당 메시지는 메시지큐에서 제거됩니다.

 

루퍼(Looper)

루퍼는 안드로이드에서 메시지 큐를 생성하고 관리하는 클래스입니다. 루퍼는 무한 루프를 실행하면서 메시지 큐에서 메시지를 가져오고 이를 처리합니다.

Looper.prepare() 메소드를 호출하여 해당 스레드에  루퍼객체를 생성합니다.  RuntimeException의 에러메시지와 같이 "스레드마다 1개의 루퍼만이 생성되어 집니다." 그리고 new Looper()를 통하여 메시지큐를 생성합니다.

/** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

 

new Looper() 생성자를 통하여 루퍼객체가 생성하는 동시에 메시지큐도 함께 생성됩니다.

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

 

이렇게 생성된 루퍼는 Looper.loop() 를 호출하여 무한 루프를 실행하고 메시지큐에 쌓인 메시지를 처리합니다. 루퍼가 메시지를 가져올 때는 메시지큐에서 가장 앞에 있는 메시지를 가져오고 가져온 메시지에 연결된 핸들러의 dispatchMessage()를 호출하고(msg.target.dispatchMessage(msg)) dispatchMessage()에서

handleMessage()가 호출되어 해당 메시지를 처리합니다.

즉, handleMessage() 를 오버라이드하여 메시지를 처리하는 로직을 작성할 수 있습니다.

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {
    final Looper me = myLooper();
    .....
    for (;;) {
        Message msg = queue.next(); // might block
        ...
        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
        try {
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
    }
}

 

메시지에 연결된 핸들러(msg.target)핸들러에서 메시지를 보낼 때 msg.target = this를 통해 자기자신을 설정합니다.

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

 

enqueueMessage()sendMessageAtTime()에서 호출되고

/**
 * Enqueue a message into the message queue after all pending messages
 * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
 * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
 * Time spent in deep sleep will add an additional delay to execution.
 * You will receive it in {@link #handleMessage}, in the thread attached
 * to this handler.
 * 
 * @param uptimeMillis The absolute time at which the message should be
 *         delivered, using the
 *         {@link android.os.SystemClock#uptimeMillis} time-base.
 *         
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the message will be processed -- if
 *         the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 */
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

 

sendMessageAtTime()sendMessageDelayed()에서 호출됩니다.

/**
 * Enqueue a message into the message queue after all pending messages
 * before (current time + delayMillis). You will receive it in
 * {@link #handleMessage}, in the thread attached to this handler.
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the message will be processed -- if
 *         the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 */
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
 * Pushes a message onto the end of the message queue after all pending messages
 * before the current time. It will be received in {@link #handleMessage},
 * in the thread attached to this handler.
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */
public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}

 

위와 같이 3가지 핸들러, 메시지큐, 루퍼를 이용하여 안드로이드에서는 비동기 처리 및 스레드간 통신을 구현할 수 있습니다.

 

'프로그래밍 > Android' 카테고리의 다른 글

가상 디바이스(Virtual Device) 생성  (0) 2023.04.10
안드로이드 스튜디오 설정(Settings)  (0) 2023.04.10
buildToolsVersion  (0) 2023.04.06
Android의 앱 호환성  (0) 2023.04.05
compile/min/tarket SdkVersion  (0) 2023.04.05

댓글