이번주는 저번주만큼 열심히 하질 못하고 있다. 조금 방심하니 미루게 된다. 나를 위하는 것보다 남에게 보여준다는 생각이 더 컸던 것 같다. 민망해서 이번주는 안쓰려다가 더 안할 것 같아서 늦게나마 쓰기 시작한다.
1월 20일 수요일에 두더지 잡기 베이스에 '장난치지 말아요' 라는 게임의 컨셉을 더하는 걸로 확정짓고, 화면 UI로 사용할 리소스 파일들을 따고 게임 화면을 구성했다.
참고) https://jung-story.tistory.com/34
run()을 오버라이드하여 TimerTask 인터페이스를 구현한 후, Timer 클래스 인스턴스를 사용하여 타이머를 실행시킨다.
var timer = Timer()
var timerTask: TimerTask
var time = 60
var ms = 0
// run()을 오버라이드하는 TimerTask 인터페이스를 구현한다
timerTask = object : TimerTask() { // 익명 객체 생성
override fun run() {
ms -= 1
if(ms <= 0){ // 1초 경과
ms += 100 // -1ms -> 99ms
time -= 1 // 1초 감소시킨다
// 60초 모두 경과 타이머 정지
if(time <= 0){
time = 0
ms = 0
cancel()
}
}
// 10ms 단위로 남은시간 업데이트, UI 업데이트는 메인 스레드에서 한다
runOnUiThread{
scoreView.text = String.format("%02d.%02d",time,ms)
timeLeftBar.progress -= 1
}
}
}
timer.schedule(timerTask,10,10) // 10ms 후에 시작, 10ms 단위로 실행
예제를 보고 바로 사용해보려고 하니 동작 방식이 좀 알송달송했다. 그래서 아예 안드로이드 스레드에 대해 먼저 자세히 알아봤다. 그리고 이해가 되기 시작했다.
참고) https://recipes4dev.tistory.com/143?category=768056
프로그램이란? 명령어와 데이터의 집합으로 구성되어 저장장치에 저장되어있는 코드를 의미한다.
프로세스란? 운영체제에 의해 메모리에 로드되어 실행중인 상태에 있는 프로그램을 말한다.
그러면 스레드란 무엇인가? 스레드는 프로세스 안에서 각각의 '독립적인 실행 흐름'을 말한다.
스레드는 왜 필요한가?
모든 프로그램은 시작~종료까지 적어도 하나의 실행 흐름을 가지고 있다.
게다가 모바일 앱의 경우, 화면 터치등의 사용자와 상호작용이 잦기 때문에 하나의 실행 흐름만으로는 앱을 구성하기 힘들다. 따라서 2개 이상의 실행흐름을 사용, 즉 멀티스레드 필요하다.
안드로이드에서 스레드를 만드는 방법
Thread 클래스 상속
Runnable 인터페이스 구현
두 방법 모두 run 메서드를 오버라이드 하며, 성능은 동일하다.
일반적으로 run 메서드만 오버라이드하면 되거나 다중 상속이 불가능한 경우 Runnable, run() 외에 Thread 기능을 확장해야 한다면, Thread 클래스를 상속하는 방법을 쓰는 것이 권장된다.
메인 스레드란?
프로세스가 시작됨과 동시에 반드시 실행시키는 스레드를 '메인 스레드'라고 한다. 또한 화면 UI를 그리고, 업데이트 하는 작업은 반드시 메인스레드에서 이뤄지고, 또 그래야만 하기 때문에 UI 스레드라고 부르기도 한다.
왜 메인 스레드에서만 화면을 조작하는 것이 가능할까?
예를 들어, 중첩된 View가 있을 경우, 여러 스레드에서 중첩 View를 각각 그릴 경우, View가 그려지는 순서에 따라 화면이 다르게 나타날 수 있다. 즉, 충돌의 가능성이 있기 때문에 화면은 반드시 메인 스레드에서만 그리도록 되어있다.
그러면 메인 스레드가 아닌 워커 스레드에서는 어떻게 View를 처리할 수 있을까?
Handler와 Message 객체를 사용하여 스레드 간 통신을 한다.
Looper, MessageQueue, Handler, Message란?
MessageQueue - Message(메시지, 이벤트)가 발생했을 때 저장되는 자료구조로, 메인스레드에 존재한다. 개발자가 직접 구현할 일은 거의 없다.
Looper - 메인 스레드에서 메시지 큐의 메시지 수신을 대기하는 역할로, 메시지 발생시 Handler(핸들러)를 호출하여 메시지를 처리한다. 개발자가 직접 구현하는 일은 거의 없다.
Handler - 앱에서 화면 업데이트, 사용자 입력 등의 message(메시지, 이벤트)가 발생했을 때, 그 메시지에 매핑되어서 그것을 처리할 때 사용되며, 메시지큐에 메시지를 전달하는 역할도 함께 한다.
스레드에서 핸들러를 생성하면 그 핸들러는 해당 스레드와 메시지큐에 각각 연결된다.
Message - 데이터를 식별하는 식별자, 데이터들, 기타 객체를 포함할 수 있으며, 스레드 간 통신을 할 때 사용된다. 메시지를 처리할 핸들러를 사용하여 메시지를 보내면, 메시지큐에 전달되고, Looper가 매핑된 핸들러를 호출하여 메시지를 처리한다.
메인 스레드에서는 긴 작업을 수행해선 안된다.
Looper와 MessageQueue의 이유로 만약 메인 스레드에서 무한 루프나 긴 작업을 수행할 경우, 그동안 메시지 큐의 메시지 수신을 확인하고 처리하는 것이 불가능하기 때문이다.
(ex: 화면이 멈추거나 터치가 먹히지 않는다.)
따라서, 긴 작업은 워커 스레드에서 작성해야 한다.
타인의 시선이 아니다,
나를 위해 하고있는 거다.