스레드란
프로세스 내에서 실행되는 독립적인 실행 흐름이라고 한다. 하나의 프로세스에서 두개 이상의 스레드가 실행될 경우 이를 멀티스레드 프로그래밍이라고 한다.
Main Thread(기본 스레드)
안드로이드 앱이 실행되면 시스템에서 하나의 스레드를 실행한다. 이때 실행되는 쓰레드가 바로 Main Thread(UI Thread라고도 불림)이다.
Main Thread의 경우 적절한 UI 위젯들(버튼, 텍스트)에 이벤트를 전달하며 상호작용한다.
또한 모든 애플리케이션 구성 요소(Activity, Service, ContentProvider, BroadCastReceiver)가 생성되고 이 구성요소들에 대한 시스템 호출(onCreate(), onStart()….) 이 Main 스레드에서 수행된다.
안드로이드에서 스레드를 사용할 때 두 가지 주의할 점이 있다.
- UI Thread를 차단하면 안됨!
- UI Thread 외에 스레드에서 UI 관련 작업을 하면 안됨!
각각의 주의점들에 대해 이유를 알아보자.
- UI Thread를 차단하면 안됨!
앱에서 사용자와 UI 위젯들 사이의 상호작용을 담당하는 스레드 = UI Thread 라고 했다.
하지만 영상을 다운받는 버튼을 클릭하거나, 데이터베이스에 데이터를 저장하는 버튼을 클릭 했을 때 UI Thread에서 이 작업들을 실행하면
전체 UI 위젯들과의 상호작용을 담당하는 역할을 수행하지 못하고 화면이 멈추게 된다.
이때 UI Thread가 약 5초 정도 차단되면 애플리케이션이 응답하지 않습니다(ANR)라는 대화상자가 표시되면서 앱이 종료된다!
예를 들어 긴 작업을 한다고 가정하면 다음과 같이 코드를 짤 수 있다.
버튼을 여러 번 눌렀더니 아래 사진과 같이 ANR이 발생하며 앱이 종료되었다.
class ExampleActivity : BaseActivity<ActivityExampleBinding>(R.layout.activity_example) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startThread()
}
fun startThread(){
binding.btnStart.setOnClickListener{
for(i in 0..10){
Timber.d("초 확인 $i")
Thread.sleep(1000)
}
}
}
}
버튼 클릭시 UI Thread를 중단시킴
이렇게 종료되고 사용자들에게 불편함을 줄 수 있기 때문에! 절대 처리하는데 시간이 긴 작업들을 UI Thread에서 하면 안되는 것이다.
그러면 긴 작업들을 어디서 처리하냐 바로 Worker Thread(작업자 스레드)에서 처리한다.
예를 들면,
fun startThread() {
binding.btnStart.setOnClickListener {
Thread {
for (i in 0..10) {
Timber.d("초 확인 $i")
Thread.sleep(1000)
}
}.start()
}
}
Worker Thread(작업자 스레드)
네트워크 연결 및 데이터베이스 작업과 같이 시간이 오래걸리는 작업들을 할 때 ANR과 같은 문제가 발생하지 않게 하기 위해 따로 스레드를 만들어 멀티스레드 프로그래밍 방식으로 처리를 해준다.
이때 만들어진 스레드를 Worker Thread라고 한다.
그럼 Main Thread 주의점으로 들어가서
2. UI Thread외에 스레드에서 UI 작업을 하면 안됨!
공식 문서에 나온 내용에 따르면 ‘Android UI 도구 키트는 스레드로부터 안전하지 않습니다.’가 이유이다.
여러 스레드에서 TextView에 텍스트 내용을 변경시킬때 경쟁상태(Race condition)가 되고, 기대한 값이 나올 수 없는 경우가 발생할 수 있다.
그렇기 때문에 안드로이드 시스템에서 UI Thread를 제외한 나머지 쓰레드에서는 UI를 변경할 수 없도록 막는다고 한다.
하지만 그렇다고 해서 못하느냐? 그것도 아니다. Android에서는 다른 스레드에서 UI 스레드에 액세스하는 여러 방식을 제공한다. 간단하게 하나씩 적어보자면
1) runOnUiThread
Thread {
runOnUiThread {
binding.textView.text = "야호"
}
}.start()
2) Handler
val handler = Handler(Looper.getMainLooper())
Thread {
handler.post {
binding.textView.text = "Updated from Thread"
}
}.start()
3) Coroutine
fun startCoroutine() {
binding.btnStart.setOnClickListener {
lifecycleScope.launch(Dispatchers.IO) {
for (i in 0..10) {
Timber.d("초 확인 $i")
delay(1000)
}
withContext(Dispatchers.Main) {
binding.textView.text = "Completed"
}
}
}
}
이런 방법들이 있을 수 있겠다.
'Android > Android' 카테고리의 다른 글
[Android] Gradle (0) | 2024.07.15 |
---|---|
[Android] Jetpack이란? (0) | 2024.07.09 |
[Android] Hilt 정리 (0) | 2024.06.16 |
[Android] Intent(인텐트)란? (0) | 2024.05.27 |
[Android] DI - Hilt vs koin 비교 (1) (1) | 2024.05.09 |