안드로이드 쓰레드, 어떻게 사용할까?
728x90
반응형

쓰레드가 뭔지 알고 싶다면 앞글부터 읽어보자.

https://devvkkid.tistory.com/119

 

안드로이드에서 Thread가 중요한 이유

쓰레드, 쓰레드, 쓰레드... 말은 무지하게 많이 들어봤지만 제대로 들여다본 적은 없는 1인 그래서 알아봈다. 쓰레드가 뭐고 당췌 왜 중요한가... 쓰레드가 뭐시당가 먼저 프로세스(Process)를 이해해봅시다. 간..

devvkkid.tistory.com

뭐, 잘은 아니더라도 대애애애애충 쓰레드가 뭔지 알았다면 이제 사용을 해볼 차례. 특히 백그라운드 쓰레드에서 UI 변경을 하기 위해 UI쓰레드에 접근하는 방법을 알고 싶을텐데. 그것부터 해보자.,

 

쓰레드 간 작업을 전달하고 싶다면??

안드로이드 개발문서를 보면 아래와 같은 안내가 있다.

  1. UI 스레드를 차단하지 마세요.
  2. UI 스레드 외부에서 Android UI 도구 키트에 액세스하지 마세요.

2번의 외부는 백그라운드 쓰레드가 될텐데, 그렇다면 백그라운드에서 UI 변경을 하고 싶다면 도구 키트를 사용하는게 아닌 UI쓰레드에 접근을 해야한다. 그 방법도 개발문서에 적혀있다. (친절하구만)

  1. Activity.runOnUiThread(Runnable)
  2. View.post(Runnable)
  3. View.postDelayed(Runnable, long)

 

Handler

쓰레드를 이용할 때 위 3개의 방법을 사용하기 어려울 정도의 복잡성을 가진다면 Handler를 사용하도록 구글에서 권하고 있다. 서로 다른 쓰레드간 통신을 할 때 중개를 해주는 역할이라고 보면 된다. 그럼 어떻게 사용하게 될까? 최근에 사용한 나의 코드를 잠깐 보자.

class ClockHandler : Handler() {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            if (redColor) {
                textClock.setTextColor(resources.getColor(R.color.clockred, null))
            } else {
                textClock.setTextColor(resources.getColor(R.color.clockblue, null))
            }

            textClock.text = time
        }
    }

Activity에서 ClockHandler를 선언해주고 Thread 클래스를 만들어서 이 Handler에 메시지를 보내는 형태로 UI가 갱신되도록 하였다. 아래가 그 코드인데 심플한 쓰레드 구조일 때 사용할만하다.

fun runningClock() {
        var thread = Thread(Runnable {
            while (true) {
                try {
                    time = dateFormat.format(Date(System.currentTimeMillis()))
                    
                    // init : 짝수 파란색, 홀수 빨간색
                    if (secFormat.format(Date(System.currentTimeMillis())).toInt() % 2 == blueNumber) {
                        redColor = false
                    } else {
                        redColor = true
                    }

                    val message = clockHandler.obtainMessage()
                    clockHandler.sendMessage(message)

                    Thread.sleep(1000)
                } catch (e: InterruptedException) {
                    //
                }
            }
        })
        thread.start()
    }

 

AsyncTask

만약 쓰레드를 비동기로 사용하고 싶다면 AsyncTask를 사용하면 된다. 왜 이걸 사용해야 하냐면 AsyncTask의 콜백 메소드에서 UI/백그라운드 쓰레드를 명확하게 분리해주기 때문이다.

  • 백그라운드 쓰레드 : doInBackground()
  • 메인 쓰레드 : onPreExecute(), onProgressUpdate(), onPostExecute()

이렇게 명확하게 분리가 되고 쓰레드를 잘못 사용할 가능성을 최소화 했기 때문에 Thread-safe하다고 볼 수 있다. 그렇다고 AsyncTask를 남발하면 안되는데 그 이유는 다른 자료를 참고하도록 하자. (귀찮아서 그러는거...맞다)

3번만 worker thread, 즉 백그라운드에 있고 2, 5, 7, 9번은 UI 쓰레드에 있는 것을 확인할 수 있다.

Thread와 Runnable

별도로 쓰레드를 사용하고 싶을 때 Thread와 Runnable을 선언하여 사용할 수 있다. 그렇다면 어떤 기준으로 이 둘 중 하나를 선택해서 사용하면 될까?

  • Thread는 상속을 받아서 사용한다. Thread 클래스를 재정의해서 혹은 확장하여 사용하고 싶을 때 상속을 받아서 사용하면 된다. 나만의 Thread 클래스라고나 할까.
  • Runnable은 implements로 구현한다. 다중상속을 할 수 없는 경우(Java)에 사용하면 적합하다. 오직 run()을 통해 쓰레드를 간단하게 사용하고 싶다면 Runnable이 적합하다.

 

ANR (Application Not Responding)

쓰레드를 잘못 사용하면 어떻게 될까? 앱을 사용하면서 갑자기 멈춘 것처럼 아무런 작동을 안하는 경험을 해본적이 있을까? 그 경우가 바로 ANR이다. UI에 영향을 주는 이벤트가 계속 쌓여서 메인쓰레드에서 작업이 아직 처리가 다 되지 않았기에 나타나는 현상이다. 안드로이드에서는 이 경우(보통 5초 정도 걸림) 시스템상으로 앱을 종료할 수 있게 다이얼로그를 띄어주기도 한다. 이것 외에도 쓰레드를 잘못 사용하여 앱이 비정상 종료되는 경우가 있다. AniToy's Home 블로그에서 별도로 학습을 해보자.

 

혹시 쓰레드를 유지하는 방법이 궁금하다면 장범석님 블로그를 참고하자.

 

참고자료
- 안드로이드 노트 : https://brunch.co.kr/@mystoryg/84
- 정상에서 IT를 외치다 : https://black-jin0427.tistory.com/177
- 안드로이드-KR : https://android-kr.tistory.com/47
- 앱해피 : https://apphappy.tistory.com/63
- 개발자를 위한 레시피 : https://recipes4dev.tistory.com/143
- VictoryWoo's Blog : https://woovictory.github.io/2019/01/07/Android-For-Interview-4/
728x90
반응형