[ 지난 게시글 ]
지난 게시글에 이어서, 이번에는 스레드를 만들어보는 예제를 따라해보고 어떻게 쓰레드간 메시지를 주고받는지에 대해서 알아보도록 하겠다. 또한 Runnable는 어떤 특징이 있는지도 짚어보도록 하겠다.
# 시계만들기
따라하는 예제코드의 최종 목표는 다음과 같다. TextView에 시간을 1초마다 리셋하여 현재시간을 불러오게하여 시계처럼 보이게 하기다.
## 일단 만들어보기 ( 쓰레드 사용 X )
지금 생각은 이렇다. onCreate에서 while문을 무한으로 돌려서 현재 시간을 1초마다 갱신해주게 한다.
private val clockTextView : TextView by lazy {
findViewById(R.id.clockTextView)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val cal = Calendar.getInstance()
val sdf = SimpleDateFormat("HH:mm:ss")
while(true) {
val strTime = sdf.format(cal.getTime())
clockTextView.setText(strTime)
Thread.sleep(1000)
}
}
이 흠잡을때 없는 코드, 목적은 저기에 시간을 띄우는 것이지만, 막상 AVD로 돌려보면 기본 시간은 고사하고 아무것도 뜨지 않는다.
왜 그럴까? 주범은 while문으로 인한 메인 루프이다. 안드로이드의 존재하는 메인스레드에서는 어디선가 메시지큐에서 현재 액티비티의 onCreate를 꺼내올 것이고 동작을 마치고 UI를 그려줘야하지만 계속 무한루프가 이벤트가 끝나지 않고 있는 것이다.
그렇다면 해당 동작을 쓰레드에서 처리하게 해주고, 1초마다 메시지큐에 메시지를 넣고 이벤트를 동작하게 해주자
## 쓰레드 사용하기 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Runnalbe 만들기 run() 메서드 오버라이드
class NewRunnable : Runnable {
var cal = Calendar.getInstance()
var sdf = SimpleDateFormat("HH:mm:ss")
override fun run() {
while (true) {
val strTime = sdf.format(cal.time)
clockTextView.text = strTime
try {
Thread.sleep(1000)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
// 쓰레드에 새로운 Runnalbe 구동
val nr = NewRunnable()
val t = Thread(nr)
t.start()
해당 오류가 뜨면서 여전히 안된다. 이 오류는 쓰레드를 공부할때부터 강조했던 것인데, 메인 UI 쓰레드가 아닌곳에서 UI구성요소를 변경시키는 시도가 있으면 위와같이 에러를 내게 되는 것이다.
그렇다면 메인UI쓰레드에서 변경시키도록 해야한다. 어떻게 할 수 있을까? 추측해보면, 메시지에 동작을 담아 메인쓰레드로 보내면 될 것이라는 생각이 문득 든다.
## 쓰레드 사용하기 2 : 쓰레드간 통신을 이용해 구현하기
즉 1초마다 텍스트를 직접 변경하는 것이 아닌, 1초마다 메시지를 보내서 메인쓰레드에서 작업을 완료하도록 하는 것이다.
@SuppressLint("HandlerLeak")
val mHandler = object : Handler() {
override fun handleMessage(msg: Message) {
val cal = Calendar.getInstance()
val sdf = SimpleDateFormat("HH:mm:ss")
val strTime = sdf.format(cal.time)
clockTextView.setText(strTime)
}
}
thread(start = true) {
while (true) {
Thread.sleep(1000)
mHandler.sendEmptyMessage(0)
}
}
새로운 스레드에서 하는 일은 1초마다 메시지를 보내주는 게 끝이다. 특별한 코드가 실행되지는 않는다. 그러나 메인스레드에서는 그 핸들러가 보낸 메시지를 처리해야하는데, 그곳에서 현재시간을 가지고와 UI를 새로 그려주는 것이다.
성공적으로 수행됨을 볼 수 있다.
## 그렇다면
이처럼 오래걸리는 일이나 while문으로 계속해서 뭔가를 확인해야 하는 경우는 쓰레드를 만들어서 메시지를 보내고 해당 핸들러가 어떻게 처리해줄지를 정해주면 된다는 것이다.
# 궁금한점
- 코틀린에서 Runnable을 만들어서 Handler로 보내고 처리하는 과정을 조금 더 코틀린스러운 코드로 알고 싶다.
- Handler()는 이제 사용되지 않는다고 하는데 대체제로 어떤게 또는 어떤 방식으로 쓰레드를 만들어 처리하는지 알아야 할거 같다.
아래 레퍼런스에서 사진자료를 참고헀으며, 예제 코드 또한 참고하여 사용했다. 정말정말 정리를 잘해주신다.