안드로이드는 기본적으로 Main Thread (or UI Thread) 하나만으로 구성되는 싱글 쓰레드 모델로 동작한다.
🤔싱글 쓰레드 모델인 이유
만약 멀티 쓰레드 환경이라면, 여러 쓰레드에서 TextView 의 텍스트를 변경하는 상황이 발생하고 어떤 결과가 나타날 지 미지수이기 때문이다. 따라서 동작의 무결성을 보장하기 위해 타 쓰레드에서는 UI 를 건드릴 수 없고, 오로지 메인 쓰레드에서만 UI 관련 동작을 할 수 있게끔 하는 것이다.
✍🏼싱글 쓰레드 모델에서 지켜야할 포인트들
1. 메인 쓰레드 (UI 스레드) 를 블로킹해서는 안 됨
→ 메인 쓰레드를 블로킹한다는 뜻은, 사용자에게 보여지는 UI 동작을 멈춘다는 뜻이다. 메인 쓰레드가 블로킹되어 UI 동작이 멈추게 되면, 이는 표면적으로 앱 퍼포먼스 저하를 유발하게 되며 사용자 경험상 악영향을 끼친다. 따라서, 시간이 오래 걸리는 동작을 수행하는 등 메인 쓰레드를 블로킹해선 안 된다.
2. UI 관련 동작은 오로지 메인 쓰레드에서만 접근해야 함
→ UI 동작의 무결성을 보장하기 위해서
🧐Handler & Looper
메인 쓰레드는 사용자에게 보여지는 UI 동작들을 수행하고 네트워크 작업이나 데이터베이스 작업 등 무거운 동작들은 메인 쓰레드가 아닌 다른 쓰레드에서 처리해주어야 한다.
하지만 다른 쓰레드에서 수행한 결과 값을 UI에 업데이트하려면 메인쓰레드에 넘겨줘야하는데 어떻게 넘겨주지?
이럴 때 사용하는 것이 Handler 와 Looper라는 장치이다!
Handler와 Looper는 쓰레드간의 통신이 가능하게 만들어준다.
동작부터 봐보자!
1. Thread #2에 있는 메세지(=작업)을 sendMessage() 또는 post 메소드를 통해 전달한다. 2. sendMessage()로 받은 메세지(=작업)을 꺼내고 Message Queue에 차례대로 넣는다. 3. Thread #1의 Looper는 Message Queue에서 차례대로 메세지(=작업)을 뽑아 메세지가 Runnable한 객체인경우 run()메소드를 실행, Message 객체인 경우 핸들러로 전달한다. 4. Looper로부터 전달받은 메세지(=작업)을 handleMessage()를 통해 작업을 한다.
위 과정을 통해 쓰레드 내에서 Handler와 Looper는 쓰레드간 통신을 가능하게 해준다.
**Handler는 MessageQueue 와, MessageQueue 안의 메세지들을 자신에게 전달해주는Looper에 의존적인 녀석
Handler
특정 메세지(=작업)를 Looper의 Message Queue에 넣거나, Looper가 Message Queue에서 특정 메세지를 꺼내어 전달하면 이를 처리하는 기능
1. Looper로 메세지를 전달하는 경우
→ sendMessage() 메소드를 통해 Message Queue에 Message 객체 넣을 수 있음. → post 메소드를 통해 Runnable 객체 넣을 수 있음.
2. Looper로부터 메세지를 전달받아 처리하는 경우
→ Runnable 객체인 경우 : run() 메소드를 호출하여 작업 실행 → Message 객체인 경우 : handleMessage() 메소드를 호출하여 Handler가 메세지 전달 받음
Looper
하나의 쓰레드에 오직 하나의 Looper, Message Queue에 들어오는 메세지들을 하나씩 꺼내어 이를 적절한 Handler로 전달하는 역할
Message Queue: 해당 쓰레드가 처리해야 할 동작들이 '메세지' 형태로 담겨 있다. (FIFO 방식)
MessageQueue 가 비어있을 땐 아무 동작을 수행하지 않는다.
Message
스레드 간 통신에 사용되는 객체로 작은 작업 단위 사용자 상호작용을 포함한 모든 시스템 이벤트를 전달할 때 사용되는 객체.
1. Runnable 객체 : 핸들러 사용의 주 목적이 메인 스레드의 코드를 실행하는 것일 때
→실행 가능한 작업을 표현하기 위한 인터페이스 →Runnable 객체는 run() 메서드를 구현하고 있으며, 해당 메서드가 호출될 때 특정 작업을 수행
2. Message 객체 :핸들러를 통해 데이터를 전달하여, 전달된데이터 처리를위해 작성된 대상 쓰레드의 코드가 실행되도록 만드는 것
→ 메시지 객체(what, object, arg1, arg2, ...)에 값을 채워 수신 쓰레드의 핸들러에 보내고, 수신 측 쓰레드에서는 handleMessage() 메서드를 오버라이드하여 수신된 메시지 객체를 처리하기 위해 작성된 코드를 실행