안드로이드 Thread, Handler, Looper

LeeEunJae·2022년 9월 10일
1

Kotlin Project

목록 보기
7/10

Thread를 평소에 사용하긴 했지만 코드에 대한 정확한 이해를 하지 않고, 그냥 외워서 사용했었습니다. 그래서 오늘은 그동안 미뤄왔던 Thread에 대한 공부를 하려합니다.
이 글은 제가 공부한 내용을 토대로 복습하는 느낌으로 작성한 글이라 잘못된 내용이 있을 수도 있고, 두서없이 느껴질 수 있습니다.

📌 Thread

프로세스(안드로이드 앱)가 실행되면 그 프로세스는 여러개의 스레드를 가질 수 있습니다.
보통 main()을 가지는 곳에서 하나의 메인 스레드가 생성되어 순차적으로 작업을 처리하는데, 오래걸리는 작업들, 예를들어 네트워크 작업, 다운로드 같은 작업들을 처리하기 위해서 백그라운드 스레드를 생성하여 그 스레드를 통해 작업을 수행합니다.

스레드는 메인스레드와 백그라운드 스레드로 나뉘는데 메인스레드에서는 UI 를 그리는 작업을 담당하고, 백그라운드 스레드 에서는 그 외의 일들을 담당합니다.

여기서 주목할 점은 메인 스레드가 아닌 다른 스레드에서는 UI를 수정할 수 없다는 것 입니다.

만약 다음과 같이 디지털 시계를 앱으로 구현하고 싶다면 어떻게 해야할까요?

📌스레드 사용 X

class MainActivity : AppCompatActivity() {

    private var mBinding : ActivityMainBinding? = null
    private val binding get() = mBinding!!

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        while(true){
            val cal = Calendar.getInstance()
            val sdf = SimpleDateFormat("HH:mm:ss")
            val strTime = sdf.format(cal.time)
            binding.clockTextView.text = strTime

            Thread.sleep(1000)
        }

        

    }

}

단순하게 생각해보면 위의 코드처럼 하면 정상적으로 작동할 것 같다고 생각합니다.
하지만 막상 실행해보면 아무런 화면도 나오지 않습니다.
왜냐하면, 메인화면을 그리기 위해서 메인스레드에서 해당 액티비티의 완료된 onCreate를 가져와서 UI를 그려야 하는데 onCreate 는 무한 루프에 빠져있기 때문에, 메인 스레드 입장에서는 UI를 그릴 수 없는 것 입니다.

그렇다면, 스레드를 사용해서 UI를 수정해야 하는 것은 알겠는데 메인스레드에서만 UI 접근이 가능한데 그럼 어떻게 메인스레드에서 UI를 변경시키도록 해야할까요?

📌 스레드 통신을 이용해 구현하기

출처 : https://recipes4dev.tistory.com/150

다음과 같이 onCreate 안에 코드를 작성하고 실행하면 정상적으로 동작하는 것을 확인 할 수 있습니다.

        val mHandler = object: Handler(Looper.getMainLooper()){
            override fun handleMessage(msg: Message) {
                val cal = Calendar.getInstance()
                val sdf = SimpleDateFormat("HH:mm:ss")
                val strTime = sdf.format(cal.time)

                binding.clockTextView.text = strTime
            }
        }

        thread(true){
            while(true){
                Thread.sleep(1000)
                mHandler.sendEmptyMessage(0)
            }
        }

Handler(Looper.getMainLooper()) : 메인스레드와 통신하기 위한 핸들러를 생성합니다.
override fun handleMessage(msg: Message) : 핸들러에서 수신한 메시지를 처리하기 위해 오버라이드 하는 메서드 입니다. 이 함수 안에서 UI 수정을 하면 됩니다.

간단하게 요약하자면 메인 스레드와 통신하기 위한 수단으로 Handler 를 생성하고, 다른 스레드를 생성해서 그 스레드 안에서 1초에 한번씩 핸들러에 메시지를 보냅니다.
그러면 메인 스레드 입장에서는 1초에 한번씩 메시지를 수신하고, UI를 갱신합니다.

📌더 간결한 코드 runOnUIthread 사용

		thread(start = true){
            while(true){
                runOnUiThread {
                    val cal = Calendar.getInstance()
                    val sdf = SimpleDateFormat("HH:mm:ss")
                    val strTime = sdf.format(cal.time)
                    binding.clockTextView.text = strTime
                }
                Thread.sleep(1000)
            }
        }

runOnUiThread 를 사용하면 UI 접근이 가능합니다.

👀 참고 사이트

https://recipes4dev.tistory.com/150

profile
매일 조금씩이라도 성장하자

0개의 댓글