상세 컨텐츠

본문 제목

안드로이드 [Android 개발] Looper와 Handler

스마트기기개발관련/안드로이드 개발

by AlrepondTech 2011. 7. 25. 14:36

본문

반응형





출처: http://ensider.tistory.com/entry/Android-%EA%B0%9C%EB%B0%9C-Looper%EC%99%80-Handler


Android OS에서 Looper와 Handler는 Thread간의 메시지 교환 메카니즘에 관여하는 객체이다.

특정 Thread에서 looper 객체를 생성하고, loop를 돌리는 전형적인 구조는 아래와 같다.
  
class LooperThread extends Thread {
      public Handler mHandler;
      
      public void run() {
          Looper.prepare();
          
          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };
          
          Looper.loop();
      }
  }
  

Looper Class

 Looper 클래스는 Thread에 한개씩만 존재할 수 있으며, 이름에서 알수 있듯이 Event Loop 및 Message Queue를 가지는 Class 이다. Looper 클래스는 이 클래스를 생성한 Thread에 묶이게 되며, 이 관계가 성립되면 이후 바꾸거나 끊을 수 없다. 그리고, 생성된 Looper Class는 해당 Thead의 TLS(Thread Local Storage) 영역에 객체가 저장된다.  
 
 Looper의 생성은 prepare()라는 static 메소드에 의해 이루어진다. 
 아래는 Framework의 소스이다. 이미 Thread에 Looper가 만들어진 경우 Runtime Exception을 발생시킨다.
  
[frameworks/base/core/java/android/os/looper.java]
 
     public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

  Looper Class의 생성자를 보면 다음과 같다.     
     
private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
}
 
 MessageQueue를 생성해 mQueue에 저장해 두며, 현재 Thread 객체를 mThread에 저장해 둔다.
 
 이제 Message Queue를 검색하고 Event를 처리하는 looper() static 메소드를 살펴보자.
 전형적인 while 형태로, queue.next()를 통해 queue안에 처리할 Message가 있는지 검사한다. Message가 있을 경우 Messsage의 Target 객체로 Message를 dispatch시킨다. 이때 Target 객체의 Type은 Handler Class이며 뒤에서 살펴보자.
 
 [frameworks/base/core/java/android/os/looper.java]
   
 public static final void loop() {
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
        while (true) {
            Message msg = queue.next(); // might block
            //if (!me.mRun) {
            //    break;
            //}
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                msg.target.dispatchMessage(msg);
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                msg.recycle();
            }
        }
  }
 

Handler Class

 Handler Class는 Looper와는 다르게 생성한 Thread에 여러개가 붙을 수 있으며, Message Queue에 Message를 Post하고, 또 Looper가 Dispatch해 준 Message를 처리하는 일을 하게 된다. 일반적으로 Handler는
    Handler mHandler = new Handler();

로 생성하게 되는데, 그 생성자 내부를 살펴보면 다음과 같다.

[frameworks/base/core/java/android/os/handler.java]
    public Handler() {
        [....]
        
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = null;
    } 

 
Looper 클래스의 myLooper() static 메소드를 호출하는데, 이 메소드는 현재 Thread의 TLS 영역에 저장되어 있던 Looper 객체를 return 해주는 메소드이다. 
그리고, 만약 현재 Thread에 Looper가 생성되어 있지 않다면, Runtime Exception을 던진다. 따라서, User Thread라면 Looper를 prepare 시킨후 Handler를 생성해 주어야 한다. 그리고, Looper의 Queue를 자신의 mQueue 멤버에 저장해 둔다. 이는 Handler.post(msg) 등의 메소드에서 Message를 Looper의 Queue에 쌓아둘때 참조하기 위해서 이다. mCallback의 Callback 클래스를 지정해 두는 것으로 기본값은 null로 설정된다. mCallback 등은 메시지를 dispatch 받았을때 사용되는데, Handler.dispachMessage 메소드를 살펴보면 다음과 같다.

[frameworks/base/core/java/android/os/handler.java]

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }


이 메소드는 앞선 Loooper 클래스의 loop() 메소드에서 호출됨 상기하자.
메시지를 받으면 처리되는 우선 순위가 있는데, Message의 callback 이 지정되어 있으면, 그것을 실행시킨다.

handleCallback 메소드는 아래와 같다. 
    private final void handleCallback(Message message) {
        message.callback.run();
    }

사실 Message의 callback 멤버 Type은 Runnable 클래스이고, 그것의 run() 메소드를 실행하는 것이다. 
참고로, Message Queue에는 Handler.Post(Runnable)계열의 메소드를 통해 Runnable을 넣거나(Post 함수 내부에서 Message 객체로 포장된다) Handler.sendMessage(Message) 메소드를 통해 Message 객체를 넣을 수 있다.

msg.callback이 null인 경우,
            
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
            
Handler의 Callback 객체가 지정되어 있다면, Callback 객체로 메시지를 넘기고, 어느 경우에도 해당하지 않는다면, Handler Class의 handleMeessage() 메소드로 메시지를 넘겨 처리한다. 기본 Handler 클래스의 handleMessage()는 비어있는데, Handler를 사용하는 곳에서 직접 구현을 해 주면 된다. 

참고로, HandlerThread 클래스라는 것이 있는데, 사용상의 편의를 위해 내부적으로 Looper를 생성해 가지고 있는 Thread이다.

Thread, Looper, Handler의 일반적인 동작 관계를 나타내면 아래의 그림과 같다.




※ 관련 링크:


반응형


관련글 더보기

댓글 영역