[Andorid] Thread와 Runnable 그리고 Looper와 Handler

2017. 6. 4. 20:41Mobile

Thread와 Runnable

안드로이드에서 새로운 Thread에서 작업을 실행하는 방법은 크게 Thread와 Runnable 2가지로 나뉩니다.

  1. Thread Class 상속하는 방법 : Thread를 직접 상속한 후 run()을 오버라이딩 하는 방법입니다. (전통적이고 일반적인 방법)

    //Thread Class 상속
    class ThreadExample extands Thread
    {
        public void run()
        {
            //run()을 오버라이딩 후 작업내용 기술
        }
    }
    
    //Thread 실행
    ThreadExample th = new ThreadExample();
    th.start();
    
  2. Runnable Interface 구현하는 방법 : Thread를 상속하면 다른 Class를 상속받을 수 없게 됩니다. Runnable Interface로 구현하면 다른 Class를 동시에 상속할 수 있습니다. 코드 유연성을 높이고 유지보수가 쉬워지기 때문에 일반적으로 사용되는 방법입니다.
    //Runnable Interface 구현
    class RunnableExample implements Runnable
    {
        public void run()
        {
            //run() 추상메서드에 작업내용 구현
        }
    }
    
    //Runnable 실행
    RunnableExample r = new RunnableExample();
    Thread th = new Thread(r);
    th.start();
    

    Runnable은 실행해야할 작업(Method)을 표현하기 위한 목적으로 만들어진 Interface입니다. 따라서 Thread 생성 뿐만 아니라 Thread간 통신에서도 Runnable이 사용될 수 있습니다.

Looper와 Message, MessageQueue

Looper와 Message는 Thread 간에 통신하기 위한 목적으로 안드로이드에서 추가된 객체입니다.

Message는 Thread 간 통신할 정보를 담는 객체입니다.
MessageQueue는 Message를 담는 Queue를 말합니다.

Looper는 Thread 내부에서 끊임없이 루프를 반복하면서 MessageQueue에 포함된 Message를 처리 합니다. 하나의 Thread는 하나의 Looper와 연결될 수 있습니다. 다른 쓰레드에서 MessageQueue에 Message를 추가하면 Looper는 순서대로 Message를 처리합니다. Message를 처리하는 방법은 이후 소개될 Handler Class의 HandleMessage()를 오버라이드 하여 정의할 수 있습니다.

안드로이드의 MainThread(UI Thread)는 기본적으로 Looper, MessageQueue와 연결되어 있습니다. Java에서 제공하는 Thread는 개발자가 직접 Looper를 생성하고 연결해주어야 합니다.

Looper Class의 구조를 살펴보면 내부에 Thread, MessageQueue를 담는 멤버변수 mThread, mQueue를 가지고 있습니다. Looper 인스턴스가 생성될 때 실행 중인 Therad를 mThread에 담게되고, 새로운 MessageQueue 인스턴스를 생성하여 mQueue에 담게 됩니다. Looper는 loop()메서드를 포함하고 있는데, loop()메서드는 끊임없이 Loop를 돌며 MessageQueue에 전달된 Message가 있는지 확인하고, Message가 있다면 Handler 인스턴스에 Message를 전달해줍니다. (참고: 여기서 Handler 인스턴스는 Message 인스턴스 내부에 지정되어 있습니다. Message Class는 target이라는 멤버변수에 Handler 인스턴스를 담을 수 있습니다. Handler의 sendMessage()를 호출 하면 Message의 target에  Handler인스턴스가 할당되게 됩니다.)

Handler

Handler는 Message를 핸들링하기 위한 Class입니다. Handler는 전달받은 Message를 처리하기 위한 handleMessage()를 포함하고 있습니다. 또한 MessageQueue에 Message를 전달하는 sendMessage(), 그리고 Runnable을 전달하는 post(Runnable r) 메서드를 제공합니다. 하나의 Thread는 여러 개의 Handler와 연결될 수 있습니다.

Handler 인스턴스를 생성하면 Handler 생성자는 자신을 생성한 Thread의 Looper와 MessageQueue를 찾아 자신의 멤버변수 mLooper, mQueue에 할당한 후 사용하게 됩니다.

class SampleHandler extends Handler
{
   @Override
   public void handleMessage(Message msg) 
   {
     // 다른 Thread에서 전달받은 Message 처리 
   }
}

class SampleThread extends Thread
{
   public Handler mHandler;
   public void run()
   { 
      //Looper.prepare() static 메서드를 호출하면 
      //Looper 인스턴스를 생성하여 실행 중인 Thread와 연결합니다.
      Looper.prepare();

      //Handler 인스턴스를 생성하면 Handler 생성자에서
      //자신을 실행하는 Thread의 Looper를 찾고 Handler와 연결합니다.
      mHandler = new Handler()
      {
          public void handleMessage(Message msg)
          { 
             //전달받은 Message를 처리할 코드 작성
          }
      };

      //loop() 메서드를 실행하면 Looper가 루프를 돌면서 MessageQueue를 조회하고
      //MessageQueue에 포함된 Message를 발견하면 
      //연결된 Handler의 handleMessage()에 전달합니다
      Looper.loop();
   }
}


물론 개발자가 직접 어떤 Thread의 Looper를 사용할지 지정할 수 있습니다. (MessageQueue는 Looper에 포함되어 있으므로 따로 지정하지 않아도 Looper의 MessageQueue가 사용됩니다.)

class SampleHandler extends Handler
{
   @Override
   public void handleMessage(Message msg) 
   {
     // 다른 Thread에서 전달받은 Message 처리 
   }
}

class SampleThread extends HandleThread
{
   private HandleThread anotherThread;
   private SampleHandler handler;

   public SampleThread()
   {
       anotherThread = new HandleThread();
       anotherThread.start();

       //직접 특정 쓰레드의 Looper를 지정해줄 수 있다
       handler = new SampleHandler(anotherThread.getLooper());
   }
}

HandlerThread

Java에서 제공하는 Thread에는 Looper와 MessageQueue 개념이 없습니다. 따라서 위와 같이 직접 Looper와 Handler를 연결해주어야 했습니다. Android에서는 이러한 불편을 해소하기 위해 HandlerThread Class를 제공합니다. HandlerThread는 Thread를 확장한 Class로서 내부에서 Looper와 MessageQueue를 기본적으로 생성해 줍니다.

Looper, Handler, Message의 관계
※ Thread간 통신을 위한 Handler, Looper, MessageQueue의 관계