[안드로이드] Looper

hee09·2021년 10월 27일
1

안드로이드

목록 보기
13/20
post-thumbnail

이 글은 깡썜의 안드로이드 프로그래밍 책을 보고 작성하였습니다.

Looper

1. Looper의 개념

UI 스레드(메일 스레드)와 개발자가 정의한 스레드 간의 핸들러에 대한 연동을 예시로 들면서 Looper의 역할과 개념을 설명하겠습니다.

  1. 개발자 스레드에서 핸들러의 sendMessage() 함수를 호출합니다.
  2. 그러면 핸들러는 전달받은 메시지를 메시지 큐(Message Queue)에 담아주는 역할을 합니다.
  3. 루퍼는 메시지 큐에 메시지가 담기는 순간을 감지하고, 담긴 메시지를 추출해서 handleMessage() 함수를 호출합니다.

대략의 과정은 위와 같고, LooperMessageQueue의 개념은 다음과 같습니다.

  • Looper : 스레드에 대한 Message loop를 실행하는데 사용되는 클래스입니다. 기본적으로 스레드에는 메시지 루프가 연결되어 있지 않습니다. 메시지를 만들려면 실행할 스레드에서 prepare() 함수를 호출한 후 루프가 중지될 때까지 Message를 처리합니다.

  • MessageQueue : Looper에서 전송할 메시지 목록을 포함하는 클래스입니다. 메시지는 MessageQueue에 직접 추가되지 않고 Looper와 연결된 Handler 객체를 통해 추가됩니다.


정리하자면 Looper는 MessageQueue에 담겨져 있는 Message를 handleMessage() 함수의 매개변수로 전달하는 역할을 합니다. UI 스레드(메인 스레드)에는 Looper와 MessageQueue가 기본적으로 존재하여 UI 스레드와 개발자가 정의한 스레드를 핸들러를 통해 사용할 때는 Looper에 대해 선언 할 필요가 없습니다. 하지만 위의 개념에도 나와있듯이 개발자가 정의한 스레드에는 Looper와 MessageQueue가 존재하지 않습니다. 개발자가 정의한 스레드간의 연동을 핸들러 구조로 개발하려면 직접 코드에서 Looper를 준비해주어야 합니다.


2. Looper의 기본적인 코드

class LooperThreadExample : Thread() {
    var mHandler: Handler? = null
    override fun run() {
        // 현재의 스레드를 Looper로 초기화
        Looper.prepare()
        mHandler = object : Handler(Looper.myLooper()!!) {
            override fun handleMessage(msg: Message) {
                // process incoming messages here
            }
        }
        // MessageQueue를 작동
        Looper.loop()
    }
}

Thread를 상속받은 개발자 스레드를 하나 정의합니다. 이 스레드는 UI 스레드(메인 스레드)가 아니므로 Looper와 MessageQueue가 존재하지 않습니다. 우선 Looper를 사용하기 위하여 Looper.prepare() 함수를 사용하여 Looper를 초기화합니다. 그 후 Looper.loop() 함수를 이용하여 MessageQueue를 작동하면 Looper가 무한루프로 돌아가게 됩니다. 따라서 더 이상 필요 없을 때 루퍼의 quit() 함수를 사용하여 종료시켜주어야 합니다.


아래 코드는 두 개의 개발자 스레드에서 Handler를 통해 연동하는 간단한 코드입니다.

  • SendMessageThread 클래스에서는 1부터 10까지 for문을 돌며 sendMessage를 통해 그 값을 Handler에게 전달합니다.
  • LooperThread 클래스에서는 Looper와 MessageQueue를 준비해 Message를 Handler의 handleMessage()에 전달합니다.
class MainActivity : AppCompatActivity() {
    lateinit var looperThread: LooperThread
    lateinit var sendMessageThread: SendMessageThread

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        looperThread = LooperThread()
        looperThread.start()

        sendMessageThread = SendMessageThread()
        sendMessageThread.start()
    }

    // Looper를 생성하고 전달되는 Message를 처리하기 위한 스레드
    class LooperThread : Thread() {
        var looperHandler: Handler? = null

        override fun run() {
            // 현재의 스레드를 Looper로 초기화한다
            Looper.prepare()

            // Handler 객체 선언
            looperHandler = object : Handler(Looper.myLooper()!!) {
                override fun handleMessage(msg: Message) {
                    SystemClock.sleep(1000)

                    // arg1 프로퍼티를 이용하여 간단한 int형 데이터 받기
                    val data = msg.arg1

                    // what 프로퍼티를 이용하여 분기
                    when(msg.what) {
                        0 -> Log.d("Looper Test", "even data : $data")
                        1 -> Log.d("Looper Test", "odd data : $data")
                    }
                }
            }

            // 이 스레드에서 MessageQueue 작동
            Looper.loop()
        }
    }

    // Handler에게 Message를 보내는 스레드
    inner class SendMessageThread : Thread() {
        override fun run() {
            for(i in 1..10) {
                SystemClock.sleep(1000)
                // Message 객체 생성
                val message = Message()
                if(i % 2 == 0) message.what = 0
                if(i % 2 == 1) message.what = 1

                message.arg1 = i

                // sendMessage() 함수를 통해 MessageQueue에 메시지를 전달한다
                looperThread.looperHandler?.sendMessage(message)
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        
        // 루퍼 종료
        looperThread.looperHandler?.looper?.quit()
    }
}

3. 참조

Handler에게 작업을 의뢰할 때는 위와 같이 sendMessage() 말고 post() 함수도 있었습니다. 이 post() 함수에 의한 요청을 처리하는 Looper를 내장한 HandlerThread 라이브러리 클래스도 존재합니다. 즉, 개발자가 정의한 스레드에서 Looper와 MessageQueue를 사용하기 위해 작성한 코드가 HandlerThread 클래스에서는 필요가 없습니다.

profile
되새기기 위해 기록

0개의 댓글