Nuke Olaf - Log Store

[Android] 안드로이드 - 핸들러란? 본문

Android

[Android] 안드로이드 - 핸들러란?

NukeOlaf 2019. 12. 29. 17:01

1. handler 의 사전적 의미

handler

- a person who trains and is in charge of animals, especially dogs

- someone who advises someone important

- someone who carries or moves things as part of their job

무언가를 조련하고, 취급하고, 조언하는 사람이라는 뜻이다. 안드로이드에서 말하는 Handler 랑은 다른 의미인것 같다. "handler" 의 어원인 "handle" 의 경우 무언가를 만지고, 다루는 늬앙스의 느낌이 강한데, 안드로이드에서 말하는 handler 도 이와 비슷하게 앱 내부에서 무언가를 다룬다는 의미가 있는 것 같다.

 

2. IT 사전에서 말하는 Handler

 

 

https://developer.android.com/reference/android/os/Handler

 

3. 그래서, Handler 란 무엇인가?

핸들러란, worker thread 에서 main thread 로 메시지를 전달해주는 역할을 하는 클래스이다.

핸들러는 핸들러 객체를 만든 스레드와 해당 스레드의 message queue 에 바인딩된다. Message queue 는 핸들러가 전달하는 message 를 보관하는 FIFO(First In First Out) 방식의 큐이다. 다른 스레드에게 메시지를 전달하려면 메시지를 전달하려는 스레드에서 생성한 핸들러의 post() 와 sendMessage() 등의 함수를 사용해야한다. 그래야 수신대상 message queue 에 메시지가 저장되기 때문이다.

message queue 에 저장된 message 나 runnable 은 Looper 가 들어온 순서대로 꺼내서 핸들러에게 전달해준다. 그러면 핸들러는 handlerMessage() 메서드를 이용하여 Looper 에게서 받은 message 나 runnable 을 처리하게 되는 것이다.

 


<개발자의 레시피에서 설명하는 핸들러 구성요소>

핸들러(Handler)의 동작을 이해하기 위해서는 각 요소들의 역할에 대해 알아둘 필요가 있습니다.

3.1 메시지. (Message, android.os.Message)

스레드 통신에서 핸들러를 사용하여 데이터를 보내기 위해서는, 데이터 종류를 식별할 수 있는 식별자와 실질적인 데이터를 저장한 객체, 그리고 추가 정보를 전달할 객체가 필요합니다. 즉, 전달할 데이터를 한 곳에 저장하는 역할을 하는 클래스가 필요한데요, 이 역할을 하는 클래스가 바로 Message 클래스입니다. (https://developer.android.com/reference/android/os/Message)

하나의 데이터를 보내기 위해서는 한 개의 Message 인스턴스가 필요하며, 일단 데이터를 담은 Message 객체를 핸들러로 보내면 해당 객체는 핸들러와 연결된 메시지 큐(Message Queue)에 쌓이게 됩니다.

3.2 메시지 큐. (MessageQueue, android.os.MessageQueue)

메시지 큐(Message Queue)는 이름 그대로 Message 객체를 큐(Queue) 형태로 관리하는 자료 구조를 말합니다. 큐(Queue)라는 이름답게 FIFO(First In First Out) 방식으로 동작하기 때문에, 메시지는 큐에 들어온 순서에 따라 차례대로 저장됩니다. (First In). 그리고 가장 먼저 들어온 Message 객체부터 순서대로 처리됩니다. (First Out).(https://developer.android.com/reference/android/os/MessageQueue)

안드로이드의 메시지 큐는 MessageQueue 클래스에 구현되어 있으며, 앱의 메인 스레드에서 기본적으로 사용되고 있습니다. 하지만 개발자가 MessageQueue 객체를 직접 참조하여 메시지를 전달하거나, 메시지를 가져와서 처리하지는 않습니다. 메시지 전달은 메시지 큐에 연결된 핸들러(Handler)를 통해서, 그리고 메시지 큐로부터 메시지를 꺼내고 처리하는 역할은 루퍼(Looper)가 수행하기 때문입니다.

3.3 루퍼. (Looper, android.os.Looper)

MessageQueue는 Message 객체 리스트를 관리하는 클래스일 뿐, 큐에 쌓인 메시지 처리를 위한 핸들러를 실행시키지는 않습니다. 메시지 루프, 즉, 메시지 큐로부터 메시지를 꺼내온 다음, 해당 메시지와 연결된 핸들러를 호출하는 역할은 루퍼(Looper)가 담당합니다. "루퍼(Looper)"라는 이름에서 알 수 있듯이, 메시지 처리를 위한 메시지 루프(Message loop)를 실행하는 것이죠. (https://developer.android.com/reference/android/os/Looper)

안드로이드 앱의 메인 스레드에는 Looper 객체를 사용하여 메시지 루프를 실행하는 코드가 이미 구현되어 있고, 해당 루프 안에서 메시지 큐의 메시지를 꺼내어 처리하도록 만들어져 있습니다. 메인 스레드에서 메시지 루프와 관련된 코드를 개발자가 추가적으로 작성할 필요는 없는 것이죠. 개발자가 할 일은, 메인 스레드로 전달할 Message 객체를 구성하고, 스레드의 메시지 큐에 연결된 핸들러(Handler)를 통해 해당 메시지를 보내기만 하면 됩니다.

3.4 핸들러(Handler, android.os.Handler)

핸들러(Handler)는 스레드의 루퍼(Looper)와 연결된 메시지 큐로 메시지를 보내고 처리할 수 있게 만들어줍니다. 메인 스레드의 메시지 처리 흐름에서, 메시지 전달과 처리를 위해 개발자가 접근할 수 있는 창구 역할을 수행한다고 할 수 있죠.(https://developer.android.com/reference/android/os/Handler)

스레드와 연관된 핸들러를 얻기 위해서는, 간단하게 new 키워드를 사용하여 Handler 클래스 인스턴스를 생성하기만 하면 됩니다. 그러면 새로운 Handler 인스턴스는 자동으로 해당 스레드와 메시지 큐에 연결(bound)되고, 그 시점부터 핸들러를 통해 메시지를 보내고 처리할 수 있게 됩니다.

출처 : https://recipes4dev.tistory.com/166


* 하나의 스레드는 하나의 고유한 Looper 와 Message Queue 만을 가질 수 있다. 그러나 하나의 스레드에서 가질 수 있는 handler 의 숫자는 제약이 없다. main thread 는 Looper 를 이미 가지고 있어서 개발자가 관리하지 않아도 되지만, worker thread 에서는 Looper 를 직접 작성하고 실행시켜야 한다.

https://black-jin0427.tistory.com/177

 

 

4. Handler 를 왜 사용할까?

핸들러는 UI 작업을 main thread 가 아닌, worker thread 에서 하기 위해 사용한다고 볼 수 있다. 엄밀히 말하자면, 꼭 UI 가 아니더라도 하나의 thread 에서 다른 thread 에게 데이터를 전달해주는 역할을 해주는 것이 바로 handler 이다. 이렇게 스레드 끼리 데이터를 전달하는 것을 스레드 통신 (Thread Communication) 이라고 한다. 스레드 통신을 위해 핸들러를 사용할 때, 통신의 대상이 다른 스레드가 아니라 자기 자신이 될 수 있다는 점도 기억해야함.

안드로이드 어플리케이션에서 사용자에게 보여지는 모든 UI의 작업은 Main Thread(UI Thread) 에서만 진행된다. 왜 안드로이드를 만든 개발자들은 UI 를 Main Thread 에서만 바꿀 수 있도록 설계했을까? 그 이유는 하나의 뷰에 여러군데에서 동시다발적으로 접근했을 때의 문제점을 없애기 위해서이다.

예를 들어서 내가 버튼을 눌러도 textview 의 숫자가 카운트가 되고, 1초마다 자동으로 textview 의 숫자가 카운트가 되는 그런 쓸데없는 앱을 하나 만들었다고 생각해보자. 이 앱에는 내가 숫자를 카운트하길 기다리는 main thread 와, 1초마다 textview 의 숫자를 업데이트해주는 worker thread 가 존재할 것이다. 그런데, 여기서 main thread 뿐만 아니라 worker thread도 textview에 언제든지 접근해서 업데이트해줄 수 있다고 생각해보자. 만약 내가 버튼을 누르는 시점과 worker thread 에서 1초가 지났다고 판단하여 textview 를 업데이트해주는 시점과 완벽하게 동일하다면? 어떤 일부터 처리할 수 있을지 모르기 때문에 문제가 생길것이다. 안드로이드는 이러한 문제를 방지하기 위해 main thread 에서만 UI에 접근할 수 있도록 설계했다. main thread 가 언제나 UI 를 보여주는 것은 아니지만, main thread 의 이러한 특징 때문에, 개발자들은 main thread 를 UI thread 라고도 부르는 것이다. 

번외로, 안드로이드의 이러한 특징 때문에, UI 를 처리하는 Main thread 에서 시간이 오래 걸리는 작업을 수행하면, UI 처리가 늦게 이루어지게 된다. 이것을 반응성이 떨어진다고 말하는데, 반응성이 떨어지면 사용자는 답답하게 느끼기 때문에 시간이 오래 걸리는 무거운 작업들은 되도록 main thread 에서 하지 않는 것이 좋다.

그러면, main thread 가 아닌 다른 스레드 (개발자들은 이를 worker thread 라고 말한다) 에서 UI 를 업데이트하고 싶다면 어떻게 해야할까? worker thread 에서 한 일을 메시지로 handler 에 전달하면 된다. worker thread 에서 작업한 내용을 handler 에 메시지로 전달하면, handler 는 그 메시지를 다시 main thread 로 전달해준다. 바로 이러한 이유 때문에 핸들러를 사용하는 것이다.

https://brunch.co.kr/@mystoryg/84

 

5. Handler 를 어떻게 사용하는가?

(1) 메시지를 전달받아야 하는 (수신대상)스레드에서 핸들러 객체를 생성한다. - 보통은 main thread

핸들러 사용시 중요하게 생각해야 하는 점이, 핸들러는 핸들러를 생성한 스레드의 루퍼와 메시지 큐에 바인딩 된다는것이다. 그래서 만약 메인스레드에 메시지를 전달하고 싶으면, 메인스레드에서 핸들러 객체를 생성해야 한다.

(2) 핸들러에서 수신한 메시지를 처리하기 위해 핸들러 객체안에 handlerMessage() 메서드를 오버라이딩 한다.

(3) 메시지를 보낼 스레드를 만든다 - 보통은 worker thread

(4) 메시지를 보낼 스레드 안에서 수신 대상 스레드의 핸들러 객체를 참조하고, obtainMessage() 메서드를 이용해 참조한 핸들러의 메시지 객체를 획득한다.

(5) obtainMessage() 로 획득한 메시지 객체에 보내고자하는 데이터를 채우고, sendMessage() 메서드로 메시지 객체를 수신대상 스레드에 전달한다.

Comments