안드로이드의 스레드는 다음과 같이 크게 두 가지로 나뉩니다.
- Main Thread (UI Thread)
- Background Thread
메인 스레드는 1개만 존재하고, 백 그라운드 스레드는 여러 개가 존재할 수 있습니다.
스레드는 Process(프로세스) 내에서 순차적으로 실행되는 실행 흐름의 최소 단위이다.
안드로이드 시스템은 새로운 앱 실행 시 새로운 리눅스 프로세스를 시작한다.
기본적으로 메인 activity를 비롯한 모든 component는 단일 process 및 Main Thread에서 실행된다.
Main Thread의 특징
1. 애플리케이션의 기본 실행 스레드
2. 대부분의 애플리케이션 코드가 실행
3. 화면의 UI를 그리는 담당
4. 안드로이드 UI 툴 킷 구성요소와 상호작용하고, UI Event를 사용자에게 응답하는 스레드
5. UI Event 및 작업에 대해 수 초 내 (일반적으로 5~10초 사이) 에 응답하지 않으면 안드로이드 시스템은 ANR (Application Not Responding) 팝업 창을 표시
5번의 특징을 보았을 때, 시간이 오래 걸리는 코드는 새로운 스레드를 생성해 처리해야 함을 알 수 있다.
Why?
지속적이고 유연한 사용자 환경을 제공하기 위해 존재한다. 사용자는 1초의 지연으로도 불편함을 느낄 수 있기 때문에 좋은 앱 서비스는 처리 자체에 지연이 존재해도, 사용자와 지속적으로 상호작용할 수 있어야 한다.
Background Thread 사용 이유
: 네트워크 작업, 파일의 업로드와 다운로드, 이미지 처리, 데이터 로딩 등의 작업의 모든 자세한 처리 시간 계산은 힘들다.
따라서 메모리 이외의 곳에서 데이터를 가져오는 작업을 Background Thread에서 수행한다.
★백그라운드 스레드로 메인 스레드에 집중될 수 있는 코드를 분산하여 더 효율적인 애플리케이션을 만들 수 있다.
1. Thread 객체를 통해 생성하는 경우
Thread 클래스를 상속 받아서 스레드를 생성
//예제 1
class WorkerThread: Thread() {
override fun run() {
var i = 0
while (i < 10) {
i += 1
Log.i("WorkerThread", "$i")
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
var thread = WorkerThread() //Thread 객체 생성
thread.start() //start()로 thread 실행
}
2. Runnable Interface 구현
Runnable Interface 구현으로 Thread 생성
Runnable Interface란?
: 다중 상속을 허용하지 않는 코틀린 언어의 특성상 상속 관계에 있는 클래스도 구현할 수 있도록 지원하는 모델
//예제 2
class WorkerRunnable: Runnable {
override fun run() {
var i = 0
while (i < 10) {
i += 1
Log.i("WorkerThread", "$i")
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
var thread = Thread(WorkerRunnable) // 차이점
thread.start()
}
3. 람다식으로 Runnable 익명객체 구현
인터페이스 내부에 메서드가 하나만 있는 경우는 람다식으로 변환이 가능
Runnable 인터페이스를 이용한 스레드는 람다식으로 변환이 가능
//예제 3
Thread {
var i = 0
while (i < 10) {
i += 1
Log.i("WorkerThread", "$i")
}
}.start() //람다식
람다식(Lambda Expression)?
: 이름이 없이도 함수의 역할을 수행하는 익명 함수
4. Kotlin에서 제공하는 thread() 구현
thread() 안에 파라미터로 start = true를 전달하면 thread() 안의 코드 블록이 실행된다.
//예제 4
thread(start = true) {
var i = 0
while (i < 10) {
i += 1
Log.i("WorkerThread", "$i")
}
}
★백그라운드 스레드는 UI구성요소에 접근하면 안된다.
//예제 5
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
Thread {
var i = 0
while (i < 10) {
binding.textView.text = "$i" //UI 요소에 접근
i += 1
Log.i("WorkerThread", "$i")
}
}.start()
}
예제 5의 코드와 같이 UI에 접근 시
"Only the original thread that create a view hierarchy can touch its view" 라는 error message가 뜬다.