ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Android] 스레드(Thread)와 핸들러(Handler), 작업 스레드에서 UI 업데이트 요청하기
    Android 2021. 4. 14. 16:32
    반응형

     

    스레드(Thread)

    스레드는 프로세스 내부에서 자원을 공유하며 실행되는 흐름의 단위다.

    안드로이드에서는 메인 스레드는 UI 스레드로써 UI 작업을 담당하기 때문에

    메인 스레드로 장기적인 작업을 수행할 경우 ANR이 발생한다.

    * ANR (Application Not Responding) : 말그대로 어플리케이션 응답 없음.

      메인 스레드가 어떤 Task에 오랜 시간 잡혀 있을 때 발생한다. 어플리케이션이 응답없다는 팝업 메세지와 함께 어플리케이션이 종료된다.


    멀티 스레드 (Multi Thread)

    프로세스는 둘 이상의 스레드를 가질 수 있는데, 여러 개의 스레드는 프로세스 내 자원을 공유하면서 동시에 작업하며 효율적으로 동작한다.

    안드로이드에서는 UI를 차단하지 않기 위해 네트워크 통신같은 작업은 주로 서브스레드를 생성해서 수행한다.

     

    그리고 표준 자바의 스레드를 그대로 사용할 수 있기 때문에 아래와 같이 간단하게 생성할 수 있다.

    Thread thread = new Thread(new Runnable() {
        public void run() {
            //work...
        }
    }).start();

     

     

    단, 메인 스레드가 아닌 다른 스레드에서는 UI를 업데이트할 수 없다. (동기화 문제로 차단)

    그래서 runOnUiThread, View.post와 같은 메서드로 메인 스레드에 접근해서 업데이트하거나,

    작업 스레드에서 Handler, AsyncTask를 이용해 메인 스레드와 상호작용한다.

     


    핸들러(Handler)

    핸들러의 역할은 간단하게 "메세지를 전달"하는 기능이다.

     

    obtainMessge를 통해 핸들러가 속한 스레드의 메세지 큐의 메세지 객체를 가져올 수 있고,

    여기에 데이터를 담아 sendMessge로 메세지 큐에 전달하여 추가할 수 있다.

    Messge msg = handler.obtainMessge();
    
    Bundle bundle = new Bundle();
    bundle.putString("data", data);
    msg.setData(bundle);
    
    hendle.sendMessage(msg); //핸들러로 메세지 큐로 메세지 전달

     

    그리고 루퍼(Looper)가 메세지 큐의 메세지를 순서대로 처리하게 되는데,

    해당 스레드의 핸들러의 handleMessge() 메서드에 해당 메세지를 전달하여 처리한다.

    * 메인스레드는 기본적으로 루퍼를 가지지만, 생성한 서브 스레드에서 루퍼를 사용하고 싶다면  생성하여 사용해야 한다. 

    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        //...
    }

     

     

    그리고 핸들러의 post() 메서드를 사용하면 Runnable 객체를 메세지 큐로 전달할 수 있다.

    handler.post(new Runnable() {
        @Override
        public void run() {
            //work...
        }
    });

    메세지와 동일하게 메세지 큐에 저장되고,Runnable 객체의 run 메서드가 해당 스레드에서 수행된다.

     

     

    그림으로 나타내면 아래와 같다.

     

     


    작업 스레드에서 UI 업데이트 요청하기

    앞에서 언급했던 것처럼 동기화 문제로 작업 스레드에서는 UI를 업데이트할 수 없고,

    runOnUiThread, handler와 같은 방법으로 메인스레드를 통해 UI를 업데이트한다. 

     

    핸들러(Handler) 사용

    위에서 설명했으니 스킵! 🥰

     

    runOnUiThread

    Activity 클래스의 메서드인 runOnUiThread는 UI 스레드로 지정된 작업을 수행한다.

    현재 스레드가 UI스레드일 경우 즉시 실행하고, 작업 스레드일 경우 UI 스레드의 이벤트 큐에 추가한다.

    아래 코드는 runOnUiThread의 실제 구현부.

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

     

    메서드 사용은 아래와 같이 파라미터로 실행할 작업(Runnable)을 전달한다.

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            //work in UI thread...
        }
    });

     

    View.post

    View 객체의 post 메서드는 파라미터로 Runnable 객체를 받아 메인스레드의 메세지 큐에 추가한다.

    view.post(new Runnable() {
        @Override
        public void run() {
            //work in UI thread...
        }
    });

     

    * 핸들러와 View.post 세가지 방식은 크게 차이가 없는데, View.post 함수는 attach 시점을 확인한다는 점이 다르다.

    더보기
    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
    
        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

    post 메서드의 실제 구현부. Attach가 됐는지 확인하고 아닐 경우 연기한다. 

    [참고] 야토팍 블로그 - UI 스레드 사용법 

     

    AsyncTask (deprecated)

    AsyncTask 클래스는 작업 스레드에서 작업을 하기 전후로 메인 스레드에서 작업을 하여 UI를 업데이트를 할 수 있다.

     

    다른 방법에 비해 정말 간단하게 구현할 수 있다는 장점이 있지만,

    재사용 불가능, 메모리 누수, 메인 스레드에서만 호출 가능 등과 같은 문제가 발생한다.

    그래서 안드로이드 11에서 AsyncTask는 공식적으로 deprecate 되었다.

    [참고] :Peter 블로그 - AsyncTask deprecated 대비하기

     

    그래도 사용 방법이 필요하다면, 아래 링크 참조 !

    https://developer.android.com/reference/android/os/AsyncTask

    댓글