안녕하세요, 반갑습니다!
한 개의 프로그램(프로세스) 에서는 많은 스레드를 동시에 처리할 수 있지요.
그리고 프로세스의 메모리 또한 공유합니다.
프로세스로부터 각 메모리를 할당 받은 스레드는 독립적으로 실행됩니다.
안드로이드 역시 1개의 앱(프로그램)에서 여러개의 스레드를 사용할 수 있는데요.
예를 들어
음악을 들을 때,
다운로드를 받을 때,
푸쉬 알림 등 이 모두 스레드를 이용한 기능입니다.
오늘은 기본적인 스레드의 생성방법과 스레드내에서 위젯 등 UI에 결과를 반영하는 방법에 대해 정리해보겠습니다.
스레드(Thread)
&
UI 에 접근하기
1. 메인 스레드
액티비티를 포함해 모든 컴포턴트가 실행되는 오직 1개만 존재하는 스레드입니다.
그래서 메인 스레드엔 제약사항이 몇 가지 있습니다.
- 화면의 UI 를 그리는 처리를 담당합니다.
- UI와 상호 작용하고, 이벤트 결과를 사용자에게 보여줍니다.
- UI 이벤트 등 작업에 일정시간 동안 응답이 없으면 *ANR 팝업을 표시합니다. (ANR : 응용 프로그램이 응답하지 않음)
사용자가 불편함을 느끼지 않도록 안드로이드는 지속적으로 상호작용을 해야합니다.
그래서 이러한 제약사항이 없다면, 사용자는 조금이라도 느린 반응에 불편함을 호소할 수 있겠지요.
2. 백그라운드 스레드
메인 스레드가 UI 처리 및 사용자와의 상호작용을 한다면,
백그라운드 스레드는 그 나머지를 모두 맡아서 처리합니다.
이를테면, 네트워크 작업 및 파일 다운로드 등 시간이 오래 걸리는 작업들이지요.
그래서 안드로이드는 종료 시간을 알 수 없는 작업들에 대해서는 이 백그라운드 스레드에서 처리하길 권장하고 있습니다.
3. 스레드 만들어보기
Thread
기본적인 스레드 생성 방법입니다.
아래와 같이 만들 수 있고, 실행하면 i 가 10번 찍히는 것을 확인할 수 있습니다.
class ThreadTest : Thread() {
override fun run() {
var i = 0
while (i < 10) {
i += 1
Log.i("TreadTest", "i = ${i}")
}
}
}
Runnable Thread
기능상 위의 스레드와 차이는 없습니다.
하지만 Thread() 를 상속받으면 기타 다른 클래스를 상속받을 수 없다는 단점이 있습니다.
반면 Runnable 을 상속받은 스레드는 다른 클래스도 구현할 수 있도록 지원하는 모델입니다.
그래서 이 Runnable 을 상속받은 스레드를 구현하는 것이 일반적입니다.
class RunnableTest : Runnable {
override fun run() {
var i = 0
while (i < 10) {
i += 1
Log.i("RunnableTest", "i = ${i}")
}
}
}
코틀린의 thread()
코틀린에서는 조금 더 간편하게 Thread() 를 사용할 수 있습니다.
thread() 안에 파라미터를 start=true 로 전달하면 블럭안의 코드를 실행할 수 있습니다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
.
.
.
thread(start = true) { //'kotlin.concurrent.thread' 를 import 해야함.
var i = 0
while(i < 10) {
i += 1
Log.d("Main", "i = ${i}")
}
}
}
}
이렇게 메인 스레드와는 별개로 작동하는 스레드를 만들어봤는데요.
안드로이드는 메인 스레드가 아닌 스레드에서는 UI에 접근할 수 없는 치명적인 규칙이 있습니다.
즉, 다른 스레드내에서 TextView 에 접근할 수 없다는 것이지요.
이제 스레드에서 처리한 작업을 UI에 반영할 수 있도록 하겠습니다.
4. runOnUiThread 로 UI 에 접근하기
가볍게 Thread.sleep() 함수를 사용해서 초단위로 TextView에 반영한 코드입니다.
이렇게 runOnUiThread 를 사용해서 간단하게 UI에 접근할 수 있습니다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
.
.
.
thread(start = true) {
var i = 0
while(i < 10) {
i += 1
runOnUiThread { //Ui에 접근할 수 있음
textView.text = "카운트 : ${i}"
}
Thread.sleep(1000) //1000 == 1초
}
}
}
}
기본적인 스레드 생성방법과 UI 에 접근하는 방법을 정리해봤는데요.
사실 runOnUiThread 는 간단한 방법입니다.
스레드의 결과를 UI 에 접근해서 조작하는 정석? 방법은 따로 있지요
바로 루퍼(Looper)와 핸들러(Handler)인데요.
차후 이 두가지를 포함해 조금 더 심도있게 스레드를 다뤄보겠습니다.
감사합니다.