
안녕하세요.
오늘은 Looper와 Handler에 대해 알아보겠습니다.
개요

Looper와 Handler는 안드로이드에서 스레드 간 통신을 관리하고,
메시지를 처리하는데 사용되는 클래스 입니다.
Main Thread와 Background Thread 간의 작업을
안전하고 효율적으로 관리할 수 있습니다.
Looper
Looper는 스레드의 메시지 루프를 관리하는 역할을 합니다.
메시지 큐가 메시지를 받을 수 있게 자신의 생명 주기 안에서 계속 돌아가게 해줍니다.
스레드 ↔ Looper는 1 : 1 매칭입니다.
메시지 루프
메시지 큐에 들어오는 메시지를 처리할 수 있도록
반복해서 메시지를 받아오는 구조
(메시지 : 하나의 작업 단위)
안드로이드의 메인 스레드는 자동으로 Looper를 생성하지만,
다른 스레드에서는 직접 Looper를 생성하고 관리해야 합니다.
주요 메서드
- Looper.prepare() : 현재 스레드에 Looper를 생성
- Looper.loop() : 메시지 큐에서 메시지를 꺼내서 반복적으로 처리하는 루프를 시작
- Looper.myLooper() : 현재 스레드의 Looper 객체를 반환
- Looper.getMainLooper() : 메인 스레드의 Looper를 반환
Looper 사용 예시
val thread = Thread {
Looper.prepare()
val hanlder = Handler(Looper.myLooper()!!)
Looper.loop()
}
thread.start()
Looper 반환 예시
1) Main Looper 반환
val mainLooper = Looper.getMainLooper()
println(mainLooper)
2) 현재 스레드 Looper 반환
val thread = Thread {
Looper.prepare()
val looper = Looper.myLooper()
println(looper)
Looper.loop()
}
thread.start()
3) 루퍼가 없는 스레드에서 호출
Looper가 없는 경우 null을 반환합니다.
val thread = Thread {
val looper = Looper.myLooper()
println(looper)
}
thread.start()
Handler
특정 스레드의 메시지 큐에서 메시지를 보내거나 처리하는 역할을 합니다.
메시지 큐에 전달된 메시지나 작업을 받아 처리하는 기능을 통해 스레드 간 통신을 도웁니다.
주요 메서드
- sendMessage(Message msg) : 메시지 큐에 메시지를 보냄
- post(Runnable r) : 메시지 큐에 실행할 Runnable 객체를 추가
- handleMessage(Message msg) : 큐에 있는 메시지가 도착했을 때 호출되며, 이를 처리하는 로직을 정의
val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
...
}
}
handler.sendMessage(Message.obtain().apply { what = 1 })
handler.post {
...
}
Handler Thread
안드로이드 Thread는 기본적으로 Looper를 가지고 있지 않습니다.
이를 개선하기 위해 나온 것이 Handler Thread 입니다.
- Looper를 자동으로 보유한 클래스
버튼을 누르면 HandlerThread에서 메인 스레드로 메시지를 전달해보겠습니다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MyApplicationTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
companion object {
const val TAG = "tistory - kdr06006"
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
// 상태 변수 정의
var count by remember { mutableStateOf(0) }
// Main Handler 정의
val mainHandler = Handler(Looper.getMainLooper()) { msg ->
when (msg.what) {
1 -> {
val result = msg.obj as String
Log.i(TAG, "메인 스레드에서 받은 응답 : $result")
}
}
true
}
fun startThread() {
Log.i(TAG, "Button Clicked!")
count++
val workerThread = HandlerThread("WorkerThread").apply { start() }
val workerHandler = Handler(workerThread.looper)
workerHandler.post {
Log.i(TAG, "workerThread에서 작업 시작")
Thread.sleep(2000)
val result = "작업 완료"
val message = Message.obtain()
message.what = 1
message.obj = result
mainHandler.sendMessage(message)
}
}
Column(
modifier = modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center
) {
// 텍스트 표시
Text(
text = "Hello $count!",
modifier = Modifier.padding(bottom = 16.dp)
)
// 버튼 추가
Button(onClick = { startThread() }) {
Text("make thread")
}
}
}
의도한대로 2초 뒤에 메인 스레드에서 정보를 받았습니다.
TID가 다른 것도 확인이 됩니다.
다른 스레드에서 작업한 것도 알 수 있습니다.
- PID : 19139
- Main TID : 19139
- Worker TID : 20445

순차성
Handler에 여러 작업이 들어오면, FIFO로 일 처리를 합니다.
=> 순차성 보장
이를 알아보기 위해 다음과 같은 예제를 만들었습니다.
- HandlerThread를 생성해 Message를 전송하는 과정을 빠르게 5번 보내고,
- 메인 핸들러에서는 각 Message를 3초간 대기 후에 처리하도록 했습니다.
val mainHandler = Handler(Looper.getMainLooper()) { msg ->
Thread.sleep(3000)
when (msg.what) {
1 -> {
val result = msg.obj as String
Log.i(TAG, "메인 스레드에서 받은 응답 : $result")
}
}
true
}
...
workerHandler.post {
Log.i(TAG, "workerThread에서 작업 시작 $count")
val result = "작업 완료 $count"
Thread.sleep(2000)
val message = Message.obtain()
message.what = 1
message.obj = result
mainHandler.sendMessage(message)
}
3초 간의 텀을 두고, 먼저 들어온 데이터부터 처리하는 것을 알 수 있습니다.
