티스토리 뷰


이 글은 PC 버전 TISTORY에 최적화 되어있습니다.


서론

 안드로이드의 UI는 기본적으로 메인스레드를 주축으로하는 싱글 스레드 모델로 동작하므로, 메인 스레드에서는 긴 작업을 피해야 합니다. 즉 긴 작업은 여분의 다른 스레드에서 실행하고 UI를 바꿀 때는 UI 스레드로 접근하도록 스레드가나 통신 방법을 사용해야합니다. 이 때 사용하는 것이 Message나 Runnable 객체를 받아와 다른 곳으로 전달해주는 Handler 클래스입니다. 이미지를 받아와 UI에 표시해줄 때, AsyncTask와 더불어 많이 사용하고 있죠.


Handler와 Looper의 필요성




 핸들러를 생성하는 스레드만이 다른 스레드가 전송하는 Message와 Runnable 객체를 받을 수 있다.


 Handler와 Looper 이 복잡한 개념이 왜 필요하나 싶으실텐데요. 위 그림을 보면 안드로이드 내에서 Thread 백그라운드 처리에 Handler와 Looper가 왜 필요한지 명확히 아실 수 있으실겁니다. 병렬 처리로 돌아가고 있는 Main과 Sub Thread에서 같은 textView 를 setText 했을 때 어떤 Thread의 것을 따라야 할까요? 이러한 동기화 문제를 처리하기 위해 안드로이드에서는 메인 스레드에서만 UI 작업이 가능하도록 제한한겁니다.  Handler와 Looper를 이용해 Sub Thread에서 Main Thread로 UI 처리 작업을 전달하던지, Main Thread 내에서 자체적으로 처리하던지 해야하는 것 입니다.




Handler와 Looper의 동작 과정

이해를 돕기 위해 필자가 이해한 내용을 바탕으로 그림 준비해봤습니다.



 Handler는 단어 의미 그대로 무언가를 처리하는 놈 입니다. 이 친구는 Message와 Runnable 객체를 처리합니다. Runnable 메시지(Thread(Runnable runnable) 생성자로 만들어서 Runnable 인터페이스를 구현한 개체를 생성하여 상속 받은 스레드는 추상 메서드 run( )을 반드시 구현해야하는 것.) 는 run( ) 메서드를 호출해 처리하고, Message는 handleMessage( ) 메서드를 이용해 처리합니다. 그럼 어떻게 흐름이 진행되는지 정리해보도록 하겠습니다.


  1.  메시지는 다른 스레드에 속한 Message Queue에서 전달됩니다.

  2.  MessageQueue에 메시지를 넣을 땐 Handlerdㅢ sendMessage( )를 이용합니다.

  3.  Looper는 MessageQueue에서 Loop( )을 통해 반복적으로 처리할 메시지를 Handler에 전달합니다.

  4.  Handler는 handleMessage를 통해 메시지를 처리합니다.

 여기서 중요한 점은 Handler는 의존적이라는 것 입니다. Handler 혼자서는 아무것도 못하는 바보이죠. MessageQueue가 있어야 하고, 또 그 메시지를 전달해 줄 Looper가 없으면 Handler의 handleMessage( )는 그야말로 거미줄을 치는 것입니다. 즉 Handler는 Thread와 Looper, MessageQueue 가 꼭 필요합니다.



Looper의 역할

Looper는 하나의 스레드만을 담당할 수 있고 하나의 스레드도 오직 하나의 Looper만을 가질 수 있습니다. Looper는 MessageQueue가 비어있는 동안은 아무 행동도 안하고 메시지가 들어오면 해당 메시지를 꺼내 적잘한 Handler로 전달합니다. 계속 반복적으로 수행하는 동작 때문에 Looper라는 이름이 붙여졌다고 합니다.


Looper 생성법
 Handler는 무조건 Looper와 MessageQueue가 필요하므로 어떤 스레드와 연동되기 위해선 Looper가 꼭 필요하다는 것을 알 수 있습니다. 그렇다면 스레드에 Looper를 포함시키려면 어떻게 해야할까요? 



Thread t = new Thread(new Runnable(){     @Override     public void run() {         Looper.prepare();         handler = new Handler();         Looper.loop();     } }); t.start();


 특정 스레드 내부에서 Looper.prepare( )를 통해 messageQueue를 준비 한 후, 원하는 Handler를 생성합니다. 그리고 run( ) 메소드의 마지막에서 Looper.loop( )를 호출함으로서 Message 전달을 기다리는 작업이 시작되게 됩니다. Activity에서 onDestroy() 때 handler.getLooper().quit()를 이용해 꼭 종료시켜주는 것도 잊지마세요.



HandlerThread t = new HandlerThread("My Handler Thread");
t.start();
handler = new Handler(t.getLooper());

 두 번째는 안드로이드에서 제공하는 HandlerThread 클래스 활용방법입니다. HandlerThread 클래스는 기본적으로 Looper를 가지고 있고 해당 Thread를 start 시키면 자동으로 Loop도 도므로 상당히 편리하게 쓸 수 있습니다.



단독 사용한 Handler?

많은 오픈소스를 보면 Handler를 Looper 없이 사용하는 경우가 있습니다. 위의 설명을 보면 거미줄만 칠게 뻔한데도요. 하지만 안드로이드에서는 편리성을 제공하기 위해 Handler의 기본 생상자를 통해 Handler 단독으로 사용할 수 있게 해줍니다. 

기본 생성자를 통해 Handler를 생성하면, 생성되는 Handler는 해당 Handler를 호출한 스레드의 MessageQueue와 Looper에 자동 연결된다.





public class MainActivity extends AppCompatActivity {

    Handler mHandler = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler();

        Thread t = new Thread(new Runnable(){
            @Override
            public void run() {
                // UI 작업 수행 X
                mHandler.post(new Runnable(){
                    @Override
                    public void run() {
                        // UI 작업 수행 O
                    }
                });
            }
        });
        t.start();
    }
}


 이런 식으로 메인 스레드에서 Handler를 생성하면 해당 Handler는 호출한 스레드의 메시지큐와 루퍼에 자동 연결 되므로 다른 스레드에서 Handler를 통해 메시지를 전달하면 메인 스레드(UI 스레드)에서 UI 작업을 가능하게 하는 것입니다.


다른 방법에 대해서는 Handler 사용법 포스팅을 참고하시길 바랍니다.




참고할만한 사이트




댓글
댓글쓰기 폼