3 분 소요

안드로이드 Thread

안드로이드에서는 Main Thread와 Sub Thread가 동시에 자원에 접근하여 생기는 동기화 이슈를 발생 할 수 있기 때문에 메인 스레드(UI 스레드) 에서만 뷰의 값을 바꿀 수 있는 싱글 스레드로 동작 한다.

-> 위와 같이 멀티 스레드 환경에서 동시에 스레드가 UI를 갱신을 하여고 한다면 TextView가 어떠한 값으로 변경될지 알 수 없다.

싱글 스레드에서 중요한 점

싱글 스레드로 동작하는 안드로이드에서 중요한 점은 따라서 아래와 같이 조심해야 하는 포인트가 있다.

❗메인 스레드(UI 스레드) 를 오랜시간 블로킹하면 안된다

-> 메인 스레드를 블로킹한다는 의미는 사용자가 보고 있는 UI가 멈춘다는 의미와 같다. 안드로이드에서는 메인 스레드를 블로킹하면 ANR(Application Not Responding)이 발생하여 강제 종료가 된다.

메인 스레드에서는 무거운 작업을 피하자

안드로이드에서 메인 스레드를 블로킹하지 않기 위해서 다른 스레드에게 사진을 서버에서 다운 받는 작업을 한다고 하자. 해당하는 사진은 결국 UI에서 보여주기 위해 메인 스레드에서 처리를 해줘야 할 것이다.

-> 이때 스레드간에 통신을 위해서는 HandlerLooper 를 통해서 처리 작업을 바꿔주어야 한다.


Handler & Looper

스레드가 시작 되면 스레드는 Handler, Looper, MessagingQueue 를 하나씩 가지고 있다. 각자의 기능은 스레드간의 통신을 위하여 사용된다.

Looper

하나의 스레드는 단 하나의 Looper를 가지며, 해당 Looper는 오직 하나의 스레드를 담당한다.

Looper의 내부에는 MessageQueue라는 것이 존재한다. 이름과 같이 MessageQueue는 스레드가 처리 해야할 일들이 Message의 형태로 들어가 있는 FIFO 구조로 동작하는 친구 이다. Looper는 MessageQueue가 비어있으면 아무 행동을 하지 않다가 Message가 들어오면 적절한 Handler에게 전달하는 역할을 한다.

Message?

  • Message 객체는 스레드가 처리하는 작업이라고 생각할 수 있다. MessageQueue 에서 이러한 작업을 넣어주거나 꺼내어 적절한 Handler 에게 전달한다.
  • Message 객체는 MessageRunnable 두개로 나누어 진다.

종합적으로 LooperMessageQueue에 있는 메시지를 꺼내어 해당하는 Handler 에게 전달하는 역할 을 한다.

Handler

Handler 는 이름과 비슷하게 어떠한 것을 다루는 일을 한다. 구체적으로 하는 일은 Looper 라는 친구의 MessagingQueue에 어떠한 값을 넣거나 Looper에서 MessagingQueue의 특정 메시지를 주면 이를 처리 한다.

1. Looper에게 메시지 전달

Looper는 MessageRunnable 객체를 담을 수 있다.

sendMessage()

  • MessageQueue에 Message 객체를 담을 수 있다.

post()

  • post 메소드를 통하여 Runnable 객체를 담을 수 있다.

2. Looper에게서 메시지를 받음

Looper의 Message Queue에서 (something)을 꺼냈을때 아래와 같이 구분이 된다.

Message

  • 해당 메시지의 내부에 있는 Handler가 가지고 있는 handleMessage()를 호출하여 Handler가 메시지를 전달 받을 수 있다.

Runnable

  • Runnablerun() 메소드를 통하여 작업을 실행할 수 있다.

정리해 보기

  1. 처음 스레드를 생성을 하여 (something)을 처리하고 UI를 갱신하기 위해서는 메인 스레드(UI 스레드) 로 전달해야 한다.
  2. 특정 스레드 HandlersendMessage() 또는 post() 메소드를 호출하여 메인 스레드의 Looper의 MessageQueue에 메시지를 전달한다.
  3. 해당 스레드의 Looper가 MessageQueue에서 Handler에게 메시지를 하나씩 전달한다.
  4. HandlerhandleMessage()를 통해 받은 메시지를 처리한다.

안드로이드에서 적접 보기

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Other Thread
        val mThread = Thread { // 익명 객체 구현
            // 여기서 UI 작업을 수행하면 Exception 발생
        }
        mThread.start()
    }
}

안드로이드의 UI관련한 작업은 메인 스레드 에서 동작한다고 하였다. 따라서 다른 스레드에서 UI 관련 작업을 하려고 하면 Exception이 발생한다.

앞서 말한 대로 Handler를 통하여 메시지를 메인 스레드의 MessageQueue에 전달 해 주어야 한다.

안드로이드에서는 메인 스레드 가 가지고 있는 Looper를 명시하는 getMainLooper()를 제공한다.

class MainActivity : AppCompatActivity() {
    private var mHandler: Handler? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 명시적으로 메인 스레드의 Looper를 생성
        mHandler = Handler(Looper.getMainLooper())
        // Other Thread
        val mThread = Thread { // 익명 객체 구현
            // 여기서 UI 작업을 수행하면 Exception 발생
            mHandler!!.post {
                // UI 작업 가능.
            }
        }
        mThread.start()
    }
}

서브 스레드에서의 작업을 메인 스레드의 Looper의 mHandler!!.post 를 통하여 메인 스레드에서 UI 작업을 할 수 있다.

runOnUiThread는 그래서?

안드로이드 runOnUiThreadActivity 클래스에서 제공되는 메소드이다. Android developer에서는 Runnable 객체를 메인 스레드에서 실행하도록 만드는 메소드로 현재 스레드가 메인 스레드이면 Runnable 객체의 run() 메소드를 직접 실행 을 하고 아니라면 Handler에게 post() 메소드를 통하여 메인 스레드로 이벤트 큐를 발송한다.

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

따라서 runOnUiThread()를 사용하면 메인 스레드가 아닐때만 Handlerpost()를 통하여 이벤트를 발생시키기 때문에 좀 더 효율적이라고 할 수 있다.

class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Other Thread
        val mThread = Thread { // 익명 객체 구현
            // 여기서 UI 작업을 수행하면 Exception 발생
            runOnUiThread {
                // UI 작업 가능.
                // UI 스레드가 아니라면 내부적으로 handler.post() 호출
            }
        }
        mThread.start()
    }
}

References

맨 위로 이동 ↑

댓글남기기