안드로이드 스레드. 핸들러와 메시지. (Android Thread. Handler and Message)

2019. 5. 27. 15:03


1. 안드로이드 메인 스레드.

[안드로이드 스레드]에서 설명한 내용 중에서 메인 스레드와 관련된 내용을 간단히 정리해볼까요?


스레드란, 프로세스 내에서 "순차적으로 실행되는 실행 흐름"의 최소 단위를 말합니다. 프로그램의 main() 함수로부터 시작되는 최초 실행 흐름 또한 하나의 스레드이며, 이를 메인 스레드라고 부릅니다.

안드로이드 앱에서 메인 스레드는 메시지 큐(Message Queue) 수신을 대기하는 루프를 실행하며, 사용자 입력과 시스템 이벤트, 화면 그리기 등의 메시지가 수신되면 각 메시지에 매핑된 핸들러의 메서드를 실행합니다.


어떠한 경우에 스레드를 사용해야 하는지, 그 판단은, 구현하고자 하는 기능이 메인 스레드와 병행적으로(Concurrently) 실행되어야 하는가를 확인하는 것입니다. 이를 다르게 말하자면, 어떤 기능을 메인 스레드에 구현했을 때, 메인 스레드 동작에 영향을 주는가를 확인하는 것이죠. 실행 시간이 오래 걸린다거나, 외부 데이터를 수신하기 위해 대기 상태에 머물러야 하는 경우, 이는 명백히 메인 스레드의 동작에 영향을 줄 수 있기 때문에 별도의 스레드로 작성해야 합니다.


그런데 메인 스레드와 병행적으로(Concurrently) 실행되어야 하는 기능을 구현하기 위해 스레드를 사용하는 것은 그리 간단한 작업이 아닙니다. 하나의 스레드를 생성하고 실행하기 위해 작성하는 코드의 단순함과는 달리, 두 개 이상의 스레드를 동시에 실행할 때는 상황이 복잡해지는데요, 두 개 이상의 스레드로 프래그래밍하는 것, 즉, 멀티 스레드(Multi Thread) 프로그래밍에서는 고려해야 할 사항과 풀어야 할 이슈들이 싱글 스레드(Single Thread) 프로그래밍에 비해 훨씬 많아지기 때문입니다.


안타깝게도, 멀티 스레드 프로그래밍에서 만날 수 있는 이슈들을 소개하고 해결 방법을 제시하는 것은 몇 개의 문장으로 끝낼 수 있는 분량이 아닙니다. 한 권의 책으로 펴내도 모자랄 정도죠. 그렇다고 미리 겁 먹을 필요는 없습니다. (대용량 서버 프로그래밍이 아닌) 일반적인 안드로이드 앱 개발 범위 내에서는, 몇 가지 이슈들에 대해서만 파악하고 있어도 큰 문제없이 멀티 스레드 프로그래밍을 할 수 있으니까요.


자, 그럼 먼저, 멀티 스레드 프로그래밍에서 가장 쉽게 만날 수 있는 이슈인 "스레드 간 통신"에 대해 살펴보겠습니다. 참고로, 멀티 스레드 프로그래밍이 사용되어야 하는 이유와 스레드 간 통신의 필요성에 대해서는 [안드로이드 스레드 예제. 스레드로 고민해보기]에서, 간단한 예제를 통해 소개하였으므로 먼저 읽어보시길 바랍니다.

2. 스레드(Thread)와 스레드 통신(Thread Communication)

프로그램에서 실행되는 모든 스레드는 기본적으로 독립적인 실행 흐름을 가집니다. 이 말은, 일단 스레드가 실행되고 나면 다른 스레드로부터 어떠한 간섭(?)도 받지 않고, 다른 스레드가 어떻게 실행되고 있는지 관심을 두지 않는다는 것을 의미합니다.


하지만 이러한 독립성은 스레드의 본질적 특성을 얘기하는 것일 뿐, 실제 상황에서는 개발자가 스레드의 실행 상태 또는 결과를 다른 스레드로 전달하도록 의도적으로 구현해야 하는 경우가 훨씬 많습니다.


예를 들어, USB 메모리에 있는 파일을 내부 스토리지로 복사해야하는 상황을 가정해보죠. ([안드로이드 스레드 예제. 스레드로 고민해보기]를 이어, 조금은 억지스러운(?) 예제를 하나 더 들어볼게요.)


파일의 크기가 기가바이트 단위인 경우, 복사 완료까지 많은 시간이 소요되므로 파일 복사 기능은 메인 스레드가 아닌 별도의 스레드를 통해 처리하기로 결정합니다. 파일 복사 스레드는 지정된 파일을 복사하도록 단순하게 구현하였습니다. 이제, 메인 화면에 "COPY" 버튼을 추가하고, 버튼을 누르면 새로운 스레드가 실행되도록 만들면 되겠네요.

스레드 통신 예제 1


자, 이제 앱을 실행한 다음 "COPY" 버튼을 클릭합니다. 그런데 당연하게도, 화면에는 아무런 표시가 되지 않습니다. 파일 복사가 시작되었는지, 얼마만큼 진행되었는지, 복사하는 과정에서 에러는 안 났는지, 복사가 완료되었는지... 도대체 알 수가 없네요. 이는 분명 사용성 측면에서 매우 좋지 않은 결과물입니다. 그래서 이제 기능을 수정하기로 결정합니다. 현재 진행 상태 또는 결과를 화면에 표시하도록 말이죠.

스레드 통신 예제 2


직관적으로 접근하여, 메인 화면에 프로그레스바(ProgressBar)를 추가한 다음, 파일 복사 스레드에서 진행 상황을 프로그레스바에 나타내도록 만들면 되겠죠? 하지만 결과는, 알다시피 "Only the original thread that created a view hierarchy can touch its views." 에러가 발생합니다. [안드로이드 스레드 예제. 스레드로 고민해보기]에서 그 과정을 직접 확인했듯이, 안드로이드에서 화면 UI 뷰 접근은 반드시 메인 스레드에서 실행되어야 하니까요.

스레드 통신 예제 3


결국 우리가 선택할 수 있는 가장 직관적이고 쉬운 방법은, 파일 복사 스레드의 진행 상태를 메인 스레드로 메시지 형태로 전달하고, 메인 스레드에서는 그 값을 사용하여 현재 진행 상태를 화면에 표시하는 것입니다.

스레드 통신 예제 4


이렇게, 하나의 스레드에서 다른 스레드로 데이터를 전달하는 것을 스레드 통신(Thread Communication)이라고 합니다.

3. 안드로이드 스레드 통신. 핸들러(Handler)

안드로이드에서 사용할 수 있는 스레드 통신 방법은 여러 가지가 있지만, 가장 일반적으로 사용할 수 있는 방법은 핸들러(Handler)를 통해 메시지(Message)를 전달하는 방법입니다. 안드로이드 핸들러(Handler)에 대한 내용은 [안드로이드 스레드 - 3.2 안드로이드 메인 UI 스레드]에서 간단하게 언급했었는데요. 안드로이드 프레임워크에 아래 그림과 같은 구조로 구성되어 있습니다. (대략적인 흐름 파악을 위한 용도로만 보시기 바랍니다.)

스레드 통신 흐름


핸들러(Handler)의 동작을 이해하기 위해서는 각 요소들의 역할에 대해 알아둘 필요가 있습니다.

3.1 메시지. (Message, android.os.Message)

스레드 통신에서 핸들러를 사용하여 데이터를 보내기 위해서는, 데이터 종류를 식별할 수 있는 식별자와 실질적인 데이터를 저장한 객체, 그리고 추가 정보를 전달할 객체가 필요합니다. 즉, 전달할 데이터를 한 곳에 저장하는 역할을 하는 클래스가 필요한데요, 이 역할을 하는 클래스가 바로 Message 클래스입니다. (https://developer.android.com/reference/android/os/Message)


하나의 데이터를 보내기 위해서는 한 개의 Message 인스턴스가 필요하며, 일단 데이터를 담은 Message 객체를 핸들러로 보내면 해당 객체는 핸들러와 연결된 메시지 큐(Message Queue)에 쌓이게 됩니다.

3.2 메시지 큐. (MessageQueue, android.os.MessageQueue)

메시지 큐(Message Queue)는 이름 그대로 Message 객체를 큐(Queue) 형태로 관리하는 자료 구조를 말합니다. 큐(Queue)라는 이름답게 FIFO(First In First Out) 방식으로 동작하기 때문에, 메시지는 큐에 들어온 순서에 따라 차례대로 저장됩니다. (First In). 그리고 가장 먼저 들어온 Message 객체부터 순서대로 처리됩니다. (First Out).(https://developer.android.com/reference/android/os/MessageQueue)


안드로이드의 메시지 큐는 MessageQueue 클래스에 구현되어 있으며, 앱의 메인 스레드에서 기본적으로 사용되고 있습니다. 하지만 개발자가 MessageQueue 객체를 직접 참조하여 메시지를 전달하거나, 메시지를 가져와서 처리하지는 않습니다. 메시지 전달은 메시지 큐에 연결된 핸들러(Handler)를 통해서, 그리고 메시지 큐로부터 메시지를 꺼내고 처리하는 역할은 루퍼(Looper)가 수행하기 때문입니다.

3.3 루퍼. (Looper, android.os.Looper)

MessageQueueMessage 객체 리스트를 관리하는 클래스일 뿐, 큐에 쌓인 메시지 처리를 위한 핸들러를 실행시키지는 않습니다. 메시지 루프, 즉, 메시지 큐로부터 메시지를 꺼내온 다음, 해당 메시지와 연결된 핸들러를 호출하는 역할은 루퍼(Looper)가 담당합니다. "루퍼(Looper)"라는 이름에서 알 수 있듯이, 메시지 처리를 위한 메시지 루프(Message loop)를 실행하는 것이죠. (https://developer.android.com/reference/android/os/Looper)


안드로이드 앱의 메인 스레드에는 Looper 객체를 사용하여 메시지 루프를 실행하는 코드가 이미 구현되어 있고, 해당 루프 안에서 메시지 큐의 메시지를 꺼내어 처리하도록 만들어져 있습니다. 메인 스레드에서 메시지 루프와 관련된 코드를 개발자가 추가적으로 작성할 필요는 없는 것이죠. 개발자가 할 일은, 메인 스레드로 전달할 Message 객체를 구성하고, 스레드의 메시지 큐에 연결된 핸들러(Handler)를 통해 해당 메시지를 보내기만 하면 됩니다.

3.4 핸들러(Handler, android.os.Handler)

핸들러(Handler)는 스레드의 루퍼(Looper)와 연결된 메시지 큐로 메시지를 보내고 처리할 수 있게 만들어줍니다. 메인 스레드의 메시지 처리 흐름에서, 메시지 전달과 처리를 위해 개발자가 접근할 수 있는 창구 역할을 수행한다고 할 수 있죠.(https://developer.android.com/reference/android/os/Handler)


스레드와 연관된 핸들러를 얻기 위해서는, 간단하게 new 키워드를 사용하여 Handler 클래스 인스턴스를 생성하기만 하면 됩니다. 그러면 새로운 Handler 인스턴스는 자동으로 해당 스레드와 메시지 큐에 연결(bound)되고, 그 시점부터 핸들러를 통해 메시지를 보내고 처리할 수 있게 됩니다.



여기까지, 핸들러(Handler)를 통해 안드로이드 스레드 통신이 수행될 때 필요한, 메시지 전달 시스템을 구성하는 요소들에 대해 간략히 알아봤는데요. 이제 안드로이드에서 제공되는 핸들러(Handler)를 사용하여 스레드 통신을 구현하는 절차와, 핸들러가 제공하는 추가적인 기능에는 어떤 것들이 있는지 좀 더 자세하게 알아보도록 하겠습니다.

4. 안드로이드 핸들러(Handler) 사용 방법.

일반적으로 핸들러(Handler)를 사용하여 스레드 통신을 수행하는 절차는 아래 그림과 같습니다. (최대한 간단한 설명을 위해, 루퍼(Looper)가 기본적으로 동작하고 있는 메인 스레드에 메시지를 보내는 경우를 가정하였습니다.)

안드로이드 핸들러 사용 절차


4.1 메시지 수신 스레드 : Handler 객체 생성 및 handleMessage() 메서드 오버라이드.

가장 먼저 할 일은 메인 스레드에서 수신 메시지를 처리하기 위해 핸들러 객체를 생성하는 것입니다.


    Handler handler = new Handler() {

    } ;


핸들러는 생성과 동시에, 코드가 실행된 스레드에 연결(bind)됩니다. 좀 더 정확히는, Handler 클래스 생성자에서 현재 스레드의 루퍼(Looper) 및 메시지 큐(MessageQueue)에 대한 참조를 가지게 되는 것인데요, 이후 단계에서 메시지를 보낼 때 이 참조를 사용하여 메시지 큐에 메시지를 넣습니다.


핸들러를 생성하고 나서 다음 할 일은, 핸들러에서 수신한 메시지를 처리하기 위해 handleMessage 메서드를 오버라이드하는 것입니다.


public class MainActivity extends AppCompatActivity {

    // 메시지 종류를 식별하기 위해, what 변수에 전달할 값을 상수로 정의.
    private final int MSG_A = 0 ;
    private final int MSG_B = 1 ;

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_A :
                    break ;
                case MSG_B :
                    break ;
                // TODO : add case.
            }
        }
    } ;
}


handleMessage() 메서드는 메시지 루프를 통해 메시지 큐(MessageQueue)로부터 꺼낸 메시지를 처리할 수 있도록, 루퍼(Looper)에 의해 실행되는 메서드입니다. 당연히, 다른 스레드로부터 전달된 데이터는 msg 인스턴스에 담겨져 있으며, 일반적으로, 정수 타입인 what 변수의 값에 따라 if 또는 switch 등의 조건문으로 처리합니다. (위의 예제 코드 참고)

4.2 메시지 송신 스레드 : 수신 스레드의 핸들러 객체 참조를 통해 메시지 객체 획득.

메시지를 보내는 곳에서는 먼저, 앞서 생성한 수신 스레드의 핸들러 객체 참조를 획득해야 합니다. 메인 스레드인 경우, 액티비티의 클래스 변수로 핸들러 객체를 선언하고, 액티비티 참조를 통해 핸들러 객체를 참조할 수 있습니다. 액티비티 내에서 스레드를 생성했다면, 핸들러 객체를 바로 참조할 수 있습니다.


    class NewThread extends Thread {
        
        Handler handler = mHandler ;

        @Override
        public void run() {

            while (true) {
                // obtain a message.
                Message message = handler.obtainMessage() ;

                // fill the message object.
                message.what = MSG_A ;
                message.arg1 = ... ;
                message.arg2 = ... ;
                message.obj = ... ;

                // send message object.
                handler.sendMessage(message) ;
            }
        }
    }


메시지 객체를 획득하기 위해서는 HandlerobtainMessage() 메서드를 사용합니다. obtainMessage() 메서드는 글로벌 메시지 풀(Global Message Pool)로부터 메시지를 가져오는데, 정적(static)으로 생성된 재사용(recycled) 객체로 관리되기 때문에 new 키워드로 새로운 Message 인스턴스를 만드는 것보다 효율적입니다.


Message 클래스에는 아래와 같은 public 클래스 변수가 존재합니다.


클래스 변수 설명
int what 메시지 종류 식별을 위한 사용자 정의 메시지 코드.
int arg1 메시지를 통해 전달되는 정수 값 저장.
int arg2 메시지를 통해 전달되는 정수 값 저장.
Object obj 수신 스레드에 전달할 임의의 객체 저장.
Messenger replyTo 해당 메시지에 대한 회신 용 메신저.
int sendingUid 메시지를 보낸 uid를 가리키는 필드.


Message 객체를 획득할 때 모든 클래스 변수를 사용해야 하는 것은 아니기 때문에, obtainMessage() 메서드는 사용할 필드 종류에 따라 여러 형태의 메서드가 존재합니다.


메서드 프로토타입 설명
Message obtainMessage() 메시지의 target이 핸들러 자신으로 지정된 Message 객체 리턴
Message obtainMessage(int what) what이 지정된 Message 객체 리턴.
Message obtainMessage(int what, int arg1, int arg2) what, arg1, arg2가 지정된 Message 객체 리턴.
Message obtainMessage(int what, Object obj) what, obj가 지정된 Message 객체 리턴.
Message obtainMessage(int what, int arg1, int arg2, Object obj) what, arg1, arg2, obj가 지정된 Message 객체 리턴.


4.3 메시지 보내기.

obtainMessage() 메서드로 획득한 메시지 객체에 보내고자 하는 데이터를 채우고 나면, 마지막으로 할 일은 Handler.sendMessage() 메서드를 사용하여 메시지 객체를 수신 스레드에 보내는 것입니다.


    // send message object.
    handler.sendMessage(message) ;


sendMessage() 메서드 또한, 파라미터, 메시지가 보내지는 시점, 메시지 큐 내 위치 등에 따라 다양한 프로토타입이 존재합니다.


메서드 프로토타입 설명
boolean sendEmptyMessage(int what) Message 클래스 변수 중 what 멤버만 채워진 Message 객체 전달.
boolean sendEmptyMessageAtTime(int what, long uptimeMillis) uptimeMillis에 지정된 시각에, what 멤버만 채워진 Message 객체 전달.
boolean sendEmptyMessageDelayed(int what, long delayMillis) 현재 시각에서 delayMillis 만큼의 시간 후에, what 멤버만 채워진 Message 객체 전달.
boolean sendMessage(Message msg) Message 객체 전달. 메시지 큐의 가장 마지막에 msg 추가.
boolean sendMessageAtFrontOfQueue(Message msg) Message 객체 전달. 메시지 큐의 가장 처음 위치에 msg 추가.
boolean sendMessageAtTime(Message msg, long uptimeMillis) uptimeMillis에 지정된 시각에, Message 객체 전달.
boolean sendMessageDelayed(Message msg, long delayMillis) 현재 시각에서 delayMillis 만큼의 시간 후에, Message 객체 전달.


리스트에 정리된 메서드들 중 AtTime, Delayed, AtFrontOfQueue 등의 접미사가 붙은 메서드는 메시지가 처리되는 시점을 조절하기 때문에, 메시지 큐 안에서의 메시지 처리 우선순위 또는 스레드 실행 대기 시간 등에 영향을 미칠 수 있습니다. 그러므로 각 메서드의 정확한 동작 방식과 그에 따른 주의사항에 대해 정확히 인지하고 사용하시길 바랍니다.


그리고 리스트에서 따로 정리하지는 않았지만, Handler 클래스에는 메시지를 보낼 때 사용하는 sendMessage() 외에 Runnable 객체를 보낼 때 사용할 수 있는 post() 메서드가 존재합니다. post() 관련 메서드를 사용하면, 상황에 따라 sendMessage() 보다 훨씬 간단하게 스레드 통신을 수행할 수 있습니다. 관련 내용은 이후에 다시 정리하는 글에서 확인하실 수 있습니다.

5. 핸들러와 관련된 몇 가지 참고 사항.

5.1 스레드 통신의 대상은 자기 자신도 포함된다.

스레드 통신을 위해 핸들러를 사용할 때, 오직 다른 스레드에서만 메시지를 보낼 수 있는 것으로 잘 못 이해하는 경우가 있는데요, 스레드 통신의 대상은 자기 자신이 될 수도 있습니다.

스레드 통신의 대상이 자기 자신


"음, 그런데 자기 자신에게 메시지를 왜 보내지? 굳이 메시지를 보내지 말고, 그냥 메서드 호출을 하면 되는 거 아닌가?"라는 의문이 들 수도 있을텐데요. 앱을 만들다보면 가끔 자기 자신에게 메시지를 보내야 하는 경우가 있습니다. 외부 스레드에서 전달되는 메시지 처리를 위해 구현한 기능을 그대로 사용하거나, 순차적으로 실행되어야 하는 코드들 사이에 시스템 이벤트가 고려되어야 하는 상황 등이 바로 그런 경우입니다.

5.2 핸들러는 스레드 당 반드시 하나만 생성해야 하는가?

아닙니다. 핸들러는 여러 개 만들 수 있습니다. 아니, 오히려 하나의 핸들러에서 모든 메시지를 처리하는 것 보다, 메시지 종류 및 기능에 따라 여러 개의 핸들러로 나누어서 처리하는 게 더 낫습니다. 물론, 메시지를 보내는 스레드에서 적절한 핸들러를 선택하여 메시지를 보내는 것은 잊지 말아야 겠지요.


    private final Handler mNetworkHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // TODO : process network message.
        }
    } ;

    private final Handler mDeviceHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // TODO : process device message.
        }
    } ;    

6. 참고.

.END.


ANDROID 프로그래밍/THREAD , , , , , , , , , , , , , , , ,

  1. 이전 댓글 더보기
  2. Blog Icon
    서지민

    개발자로써 많은 도움 받고 갑니다.

  3. 개발자로서 도움 드릴 수 있어서 다행입니다.

    방문해 주셔서 감사합니다.

  4. Blog Icon
    CJK

    감사합니다^^

  5. 방문해 주셔서 감사합니다. ^^

  6. Blog Icon
    sg

    thread 및 handler에 대한 개념이 안잡혔는데 작성자분의 글을 읽고 얼추 잡혔습니다.
    설명이 잘되어있네요. 감사합니다 :)

  7. 도움이 되셨다니 다행입니다.

    조금 더 좋은 내용 정리할 수 있도록 노력하겠습니다.

    감사합니다.

  8. Blog Icon
    사랑합니다

    중간에 코드를 보는데 수신 측에서 핸들러를 private으로 선언해서 그런가, 아니 public으로 선언해도, NewThread에서 해당 액티비티의 핸들러 객체인 mHandler를 가져올수가 없네요.. Cannot resolve가 뜨는데 어떻게 하면 해결할 수 있을까요??

  9. 아래 글에 자답 올려놓으셨네요. ^^

  10. Blog Icon
    사랑합니다

    아, 스레드 클래스 조차도, 메인 액티비티 안에서 선언된건가보네요.
    액티비티, 클래스, 스레드.. 여러 개념이 섞여있다보니 참 어렵습니다. ㅜ

  11. 소스 코드의 설명이 부족하여 조금 헷갈리셨나보네요.

    조금 더 자세하고, 친절하게 내용 정리할 있도록 노력하겠습니다.

    감사합니다.

  12. Blog Icon
    취준생

    책에서도 안 나오는 자세하고 상세한 설명과 그림 덕분에 이해가 쏙쏙 됩니다.
    이렇게 공짜로 고급 문서를 읽어도 될까 싶을 정도네요. 너무 감사드립니다.
    다시 헛갈리거나 까먹을 때마다 종종 들러서 상기시켜야겠습니다.

  13. 전체적으로 내용이 빈약하여, 많은 도움이 될지 모르겠네요.

    방문해 주셔서 감사합니다.

  14. Blog Icon
    시스템마스터

    궁금한게 있는데 handler객체는 왜 메인스레드에서만 만들어야 되나요??

  15. 조금 오해가 있으신 것 같네요.
    핸들러 객체를 메인스레드에서만 만들어야 하는 것은 아닙니다.

    어느 스레드서나 만들 수 있고, 사용할 수 있죠. 단, 스레드에서 핸들러를 지원하기 위해서는, 루퍼가 실행되고 있어야 합니다.

    좀 더 자세한 내용에 대해 알아보시려면, Looper와 관련된 내용을 찾아보시길 바랍니다.

    감사합니다.

  16. Blog Icon
    익명

    비밀댓글입니다

  17. 죄송하지만, 질문글에 작성된 내용의 의미를 잘 이해하지 못하겠습니다.

    그래도 대충 정리해보자면,

    1. 핸들러를 사용하여 앱 간 통신을 구현한다.

    2. onCreate()에서 어떤 함수 F()를 호출하는데, F()의 리턴 값이 boolean 타입이다.

    3. 그런데 F() 함수를 호출하면 false가 리턴된다.

    4. 하지만 F() 함수가 무조건 false를 리턴하는 것이 아니라, 통신을 통해 전달받은 데이터를 리턴하도록 만들고 싶다.

    으.. 질문을 조금 더 잘 남겨주셔야 제가 답을 드리기가 쉬울 것 같네요. 현재 구현하고 있는 내용을 저는 잘 모르니까요.

    어쨌든, 위의 정리가 어느 정도 맞다고 가정했을 때..

    함수 F()의 결과에 따른 실행 코드를 onCreate() 가 아닌, handleMessage()에서 실행하면 되지 않나요?

  18. Blog Icon
    익명

    비밀댓글입니다

  19. 이제 질문 내용이 깔끔하게 이해되네요. ^^

    그럼, 이 문제의 원인을 정확히 파악하기 위해서 질문을 하나 던져보죠.

    "onCreate()와 handleMessage()는 어느 스레드에서 실행될까요?"

    이 질문에 대한 답은 어렵지 않죠?
    스레드 관련 글에서 알 수 있듯이, 두 함수 모두 메인스레드에서 실행됩니다.
    onCreate()는 메인스레드에서 액티비티 초기화 이벤트 수신 시 실행되고,
    handleMessage()는 메인스레드에서 특정 Message를 수신했을 때 실행되죠.

    그럼 이제, 문제의 원인이 파악될 것입니다.
    onCreate() 함수에서 F() 함수를 실행해도 그 결과 응답 처리를 위한 handleMessage()는 onCreate()가 종료된 이후에 실행되기 때문인데요,
    왜냐하면, onCreate()와 handleMessage() 둘 다 하나의 스레드에서 순차적으로 실행되기 때문입니다.

    그렇다면 이제, 질문글에 올린 내용을 어떻게 처리해야 하나... 고민이 되실텐데요.
    뭐.. 여러 가지 구현 방법을 선택할 수 있겠지만..
    가장 간단하고 직관적으로 구현할 수 있는 방법을 고민해본다면,

    실행 여부를 onCreate()의 F()에서 판단하지 말고, F()에서는 B 앱으로 요청만 보내고,
    B앱으로부터 응답이 전달되었을 때 호출되는 handleMessage()에서 판단하도록 만드는 것입니다.
    앱의 실행 코드 등도 handleMessage() 이후에 실행되도록 만들고요.
    즉, 위 답글에서도 간단히 적었듯이, onCreate()에서 F() 함수 이후에 처리하던 작업을 handlerMessage()에서 처리하도록 만드는 것이죠.

    만족할만한 답변이 되셨는지 모르겠네요.

    추가적으로 궁금한 내용이 있으면 질문글 남겨주세요.

    감사합니다.

  20. Blog Icon
    이진성

    와 ... 최고입니다. 이런 설명 정말 원했던 설명인데 어디서도 들을수 없었죠. 감사합니다.

  21. 도움이 되셨다니, 다행입니다.
    칭찬 댓글 남겨주셔서 정말 감사합니다.

  22. 스레드를 만들어야하는데요

    메인엑티비티, 서브엑티비티1, 서브엑티비티2, 서비엑티비티3
    이렇게 있는데요.
    스레드는 메인엑티비티에서 실행 가능하다고 되어있는데요.
    근데
    핸들러 사용할때 메인에다가 작성해야하는건가요?
    아니면 핸들러를 사용해야하는 서브액티비티에 작성해서 메인엑티비티에 보내야하는건가요?

    다른사람들은 다들 잘 이해하는데 저는 왜이렇게 모자란거죠?...

  23. 스레드는 메인액티비티에서만 실행 가능한 것이 아닙니다. 어디서나 실행할 수 있습니다. 메인액티비티에서 만들 수도 있고, 서브액티비티에서 만들 수도 있습니다.

    단, 스레드로부터 이벤트를 받으려면 이벤트를 처리하고자 하는 액티비티에 핸들러를 만들고, 스레드가 해당 핸들러를 통해 메시지를 전달하도록 만들어야 합니다.

    이러한 일련의 과정이 블로그의 스레드 관련 내용들에 정리되어 있습니다.

    일단 스레드와 관련된 글들을 다시 한번 차근차근 읽어보시고, 예제를 직접 작성해서 실행해보시기 바랍니다.

    처음 스레드를 접하는 분들께는... 낯설긴 해도 그리 어렵지는 않은 개념입니다.

    감사합니다.

  24. Blog Icon
    비틀

    스레드 하나 당 여러 핸들러 생성이 가능하다 하셨는데, 그렇다면 스레드 하나당 메세지큐, 루퍼도 여러개 존재 할 수 있나요??

  25. 하나의 스레드에 메시지큐와 루퍼는 하나 씩만 존재하며, 두 개 이상 만들 필요가 없습니다. 애초에, 두 개 이상 만들 이유가 없습니다.

    감사합니다.

  26. Blog Icon
    이거보니

    이거보니 이해가 되네요 감사합니다.

  27. 방문해 주셔서 감사합니다.

  28. 맨날 모르는거 있으면 여러블로그 돌아다니다 결국엔 항상 이 블로그에서 이해하고 넘어가는듯... 감사합니다.

  29. 좋게 봐주셔서 감사합니다.
    더욱 더 알찬 내용 담을 수 있도록 노력하겠습니다.

    감사합니다.

  30. 갓뽀따님 항상 감사하게 보고 있습니다
    한가지 부탁이 있는데 갓뽀따님의 설명을 통해서 안드로이드 MVVM에 대해 공부해보고 싶습니다
    부담주는것 같아 죄송스럽지만 안드로이드 프레임워크에 대한 이해도가 높으셔서 MVVM도 쉽게 설명해주실거 같습니다
    다시한번 좋은 포스팅 감사합니다

  31. 네. 기회가 있으면 한번 다뤄보고 싶네요.
    하지만... 큰 기대는 안 하시는 게 좋을 것 같다.... 는 양해를 구해야 할 것 같네요.

    요즘 Node.js-Backend + React-Frontend 작업 중이라, 현재는 안드로이드를 직접 다루고 있지 않거든요.

    그래도 기회가 된다면... 한번 정리해볼게요.

    감사합니다.

  32. 훌륭한 글 감사합니다. 많은 도움 얻어가고 있습니다.

  33. 도움되셔서 다행이네요.
    방문해 주셔서 감사합니다.

  34. 앗 혹시 "스레드 통신의 대상은 자기 자신도 포함된다."에서 말씀하신, 앱을 만들다보면 가끔 자기 자신에게 메시지를 보내야 하는 경우에 대해서 조금 더 자세히 설명해주실 수 있나요? 이 부분이 생소해서 이해가 잘 가지 않습니다... 자신에게 메세지를 보낸다는 것이, 자신이 수행할 작업을 예약한다는 의미로 받아드려지는데, 올바른 이해인지 잘 모르겠습니다. 그럼 좋은 하루 되세요 !

  35. 충분히 생소하게 느껴지실 수 있다고 생각이 들고요.

    뭔가 예를 들어서 설명을 드리면 좋겠는데, 지금 딱히 떠오르는 케이스가 없네요.

    본문의 내용대로,
    "외부 스레드에서 전달되는 메시지 처리를 위해 구현한 기능을 그대로 사용하거나, 순차적으로 실행되어야 하는 코드들 사이에 시스템 이벤트가 고려되어야 하는 상황"인데...

    엄밀히 따지면 위의 경우도 굳이 자기자신으로의 스레드 통신을 사용하지 않아도 되거든요.

    그냥 본문의 내용대로, 스레드 통신의 대상이 자기 자신(스레드)가 될 수도 있다.. 라는 정도로만 이해해 주시면 될 것 같습니다.

    음.. 아... 설명해드리기가 쉽지 않네요.

    죄송합니다.

  36. Blog Icon
    thanks

    4번 예제 그림에서 Thread2가 Thread1의 Handler 객체를 알고 있다면, 왜 직접 handler.handleMessage() 콜백을 호출하지 않도록 설계 되었을까요?? 굳이 sendMessage()를 해서 MessageQueue를 거쳐서 복잡하게 돌아가는 이유가 뭘까요?

    제가 느끼기에 Handler는 Thread간의 BroadcastReceiver같은 느낌입니다.
    그런데 이게 맞다면 Boradcast 송신처도 생각해보니 직접 .onReceive()를 호출해주진 않네요... handler.handleMessage()를 직접 부르지 않는 이유도 인터페이스화하는 설계적인 요소일까요...?

  37. Thread2가 Thread1의 Handler 객체를 알고 있다고 해서 Thread1의 handler.handleMessage()를 Thread2가 직접 호출하게 되면 그건 Thread2에서 실행되는 것이지 Thread1에서 실행되는 것이 아닙니다. 만약 이렇게 실행되어도 아무런 문제가 발생하지 않는다면, Thread1의 존재 의미가 없어집니다. 굳이 Thread1을 만들 필요가 없다는 것이죠.

    아마 점점 새로운 내용을 습득하시면서 애초에 왜 스레드를 사용하는지에 대한 이유를 놓치신 것 같네요.

    추가적으로 궁금한 내용이 있으면 질문글 남겨주세요.

    감사합니다.

  38. Blog Icon
    익명

    비밀댓글입니다

  39. 도움이 되신 것 같아 다행이네요.
    방문해 주셔서 감사합니다.

  40. Blog Icon
    Thanks

    상세한 설명 감사합니다
    정말 큰 도움이 되었습니다!

  41. 도움되셨다니 다행이네요.
    방문해 주셔서 감사합니다.