ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • AsyncTask 그리고 Looper, Handler, HandlerThread #3
    Android Development 2017. 10. 2. 01:41

    이전 포스팅 보기

    AsyncTask 그리고 Looper, Handler, HandlerThread #1

    AsyncTask 그리고 Looper, Handler, HandlerThread #2



    안녕하세요! 이번 포스팅에서는 Looper, Handler, HandlerThread에 대해서 알아보겠습니다. 이전 포스팅에서는 안드로이드의 UI 갱신 규칙과 AsyncTask에 대해서 알아봤습니다. 이제 마지막으로 Looper, Handler, HandlerThread를 이용하여 백그라운드 작업을 처리하고 UI를 갱신하는 방법을 알아보겠습니다.


    Looper, Handler, HandlerThread

    안드로이드에서는 기본적으로 UI를 그리거나 갱신하는 일을 UI Thread에서 담당합니다. 보통 Main Thread가 UI Thread의 역할을 하고 오래걸리는 작업은 Background Thread에서 처리합니다. 

    Background Thread에서 오래걸리는 작업이 모두 처리가 완료되면 데이터를 UI Thread로 전달하여 UI 갱신을 해야합니다. 이때 데이터를 전달하기 위해 Looper와 Handler라는 것을 사용해야 합니다. 

    다음 그림을 통해 Looper와 Handler에 동작방식에 대해 알아보겠습니다.



    그림에서 보면 Thread2에서 Thread1의 Handler 객체를 통해 Message Queue로 Message 객체를 전달합니다. Looper는 Message Queue에서 Message 객체를 하나씩 꺼내서 Handler의 handleMessage() 메소드로 전달합니다. handleMessage에서는 전달받은 Message 객체를 처리합니다. 따라서 Thread2가 전달한 데이터가 Thread1에 전달되고 처리되는 것을 볼 수 있습니다.


    Looper, Handler를 사용하는 이유

    간단히 static 변수를 이용해서 데이터를 전달하면 되는데 왜 굳이 Looper와 Handler를 사용하지?라는 생각을 하실 수도 있습니다. 하지만 사용자가 직접 동기화 이슈 등의 병렬 처리 문제를 해결하는 코드를 작성해야 합니다. 코드가 매우 복잡해지고 디버깅이 어려워질 가능성이 높습니다.


    따라서 안드로이드에서 검증된 방법인 Looper와 Handler를 사용하는 것이 좀 더 깔끔하고 안전하게 Multi Thread 코드를 작성하고 사용할 수 있습니다.

    Looper 란?

    Looper는 Thread 당 하나씩만 가질 수 있는 객체로 내부에 Message Queue를 가지고 Message 객체를 하나씩 꺼내서 Message의 Target Handler로 Message를 전달하는 역할을 합니다.

    Looper.prepare() 메소드를 호출하면 자동으로 Looper 객체를 생성하여 메소드를 호출한 Thread에 객체를 할당합니다. 만일 Thread가 이미 Looper 객체를 가지고 있다면 예외를 발생시킵니다.

    Main Thread에서는 기본적으로 시작할 때 Main Looper를 할당받고 시작하므로 Looper를 가지고 있습니다. 따라서 새로운 Looper를 생성하려하면 다음과 같은 예외가 발생합니다.




    Looper의 Message 객체를 저장하는 Message Queue에서 처리 할 메세지들을 관리합니다. Message 객체를 살펴보면 여러가지 필드가 있는데 그 중 자주 사용되는 필드는 다음과 같습니다.


    what : 어떤 메세지인지 나타내는 사용자 정의 정수 값입니다. handleMessage 메소드에서 메세지의 종류에 따라 적절한 처리를 하기 위해 사용합니다.


    obj : 메세지를 전달할 때 사용자가 전달하고 싶은 객체를 설정하여 전달합니다.


    target : 해당 메세지를 처리할 Handler를 지정하는 필드값입니다.


    Looper를 실행하기 위해서는 Looper.loop() 메소드를 호출해야 합니다. 메소드를 호출하면 Looper는 Message Queue에서 Message 객체를 하나씩 순차적으로 꺼내서 Message에 Target Handler로 전달합니다.


    Handler 란?


    Handler는 Looper와 연결되어 Message 객체를 Looper의 Message Queue의 넣거나 Message를 처리하는 역할을 합니다. Handler는 반드시 하나의 Looper 객체와 연결돼야 합니다.


    Handler는 주로 new Handler() 또는 new Handler(looper)를 통해 생성합니다. Looper 객체를 전달하지 않고 기본 생성자로 객체를 생성하면 현재 Thread의 Looper를 가져와서 Handler와 연결합니다.


    Handler에서 Message 객체를 Looper의 Message Queue로 전달할 때는 post() 또는 sendMessage() 메소드를 주로 사용합니다. 


    post() 메소드는 Runnable 객체를 전달할 때 사용합니다. post() 메소드의 매개변수로 Runnable  인터페이스를 구현한 객체를 전달하면 내부적으로 Message 객체로 생성하여 queue에 넣습니다. 


    sendMessage() 메소드는 Message 객체를 전달할 때 사용합니다. sendMessage() 메소드의 매개변수로 Message 객체를 전달하면 queue에 객체를 넣습니다. 


    Looper가 Message 객체를 하나씩 꺼낼 때 Message의 Target Handler의 처리기를 실행합니다. 만약 Message 객체에 Runnable 객체가 설정되어있다면 Handler의 handlerCallback() 메소드를 호출하여 Runnable 객체를 실행합니다. Message 객체의 Runnable 객체가 설정되지 않았다면 Handler의 handlerMessage() 메소드가 호출 됩니다. handlerMessage 메소드는 재정의하여 사용해야합니다.


    예제를 통해 Looper와 Handler 사용법을 알아보겠습니다.



    예제를 실행하면 다음과 같은 화면이 나옵니다.




    버튼을 누르면 Button의 Text가 0.1초마다 1부터 100까지 변합니다. 이전 포스팅에서 UIThread에서는 너무 긴 작업을 할 수 없다고 했습니다. 또한 UIThread가 아닌 Thread에서는 UI를 변경할 수 없다고 했습니다. 


    그래서 예제에서는 버튼을 클릭하면 새로운 Thread를 만들어서 0.1초마다 Handler를 통해 Main Thread에 정수값을 넣은 메세지를 전달했고 Main Thread에서는 재정의한 Handler의 handleMessage에서 Message를 가져와서 Button의 텍스트를 전달받은 정수값으로 바꿔주었습니다.


    그런데 Handler는 반드시 하나의 Looper와 연결돼야 한다고 했었는데 예제 코드에서는 Looper와 연결하는 부분이 없습니다. 


    그 이유는 안드로이드의 Main Thread는 기본적으로 Looper를 가지고 있기 때문입니다. 또한 Handler의 기본 생성자를 이용하면 Handler를 생성한 Thread의 Looper와 연결되기 때문에 예제코드에서는 Looper와 연결하는 코드가 없습니다.


    예제 코드에서 Message 객체를 Handler의 obtainMessage 메소드로 가져왔습니다. 왜 new 키워드를 통해 Message 객체를 생성하지 않고 obtainMessage 메소드로 Message 객체를 생성할까요? Message 객체는 SendMessage 메소드로 Looper에 보내지기 전에 Target Handler를 지정해야합니다. Message 객체의 setTarget 메소드를 통해 Target Handler를 지정해야 하는데 Handler의 obtainMessage를 이용하면 이를 간단하게 해결할 수 있습니다.


    더 자세한 내용은 아래의 링크를 참조하시기 바랍니다.


    Android Handler Document

    Android Looper Doucment

    Android Message Document


    위 예제를 통해 Handler와 Looper, Message에 대해 알아보았습니다. AsyncTask에 비해 사용하기가 복잡하고 어려운데 왜 굳이 Handler와 Looper를 사용하나 하는 생각이 드실 수 있습니다.


    AsyncTask는 여러번 호출했을때 기본적으로 하나의 Thread에서 순차적으로 동작한다는 것을 이전 포스팅에서 봤습니다. 특정 옵션을 통해 병렬적으로 수행할 수도 있지만 동기화 등의 문제를 해결해야 한다는 것을 이전 포스팅에서 봤습니다. 


    그래서 병렬적으로 여러 작업을 수행할 때는 Handler와 Looper를 사용하는 것이 더 편리합니다.

    하지만 Handler와 Looper를 이용하는 것도 복잡하고 불편하게 느껴지실 수 있습니다.


    이번에는  HandlerThread에 대해 알아보겠습니다. 

    기본적인 내용은 Handler, Looper와 같으므로 바로 예제를 통해 알아보겠습니다.


    HandlerThread 란?



    Handler와 Looper를 이용하는 것과의 차이는 HandlerThread는 Looper를 만들어 준다는 것입니다. 따라서 onLooperPrepared 메소드에서 Looper가 준비되면 Handler를 생성하면 됩니다.


    또한 HandlerThread는 내부적으로 무한루프를 돌면서 메세지가 들어오면 처리해주는 역할을 해주는 Thread 입니다. 


    더 자세한 내용은 아래의 링크를 참조하시기 바랍니다.


    Android HandlerThread Document


    따라서 Thread를 이용하는 상황에 따라 AsyncTask, Handler와 Looper, HandlerThread 중 하나를 골라서 이용하시면 간단하게 오래걸리는 작업을 수행하고 UI를 바꿀 수 있습니다.


    미흡하지만 끝까지 읽어주셔서 감사합니다. 더 좋은 포스팅을 쓰도록 노력하고 공부하겠습니다. 감사합니다.


    댓글

Designed by Tistory.