Android에서 Looper, Handler 및 MessageQueue의 관계는 무엇입니까?


95

Looper, Handler및에 대한 공식 Android 문서 / 가이드를 확인했습니다 MessageQueue. 그러나 나는 그것을 얻을 수 없었다. 나는 안드로이드를 처음 접했고 이러한 개념과 매우 혼동되었습니다.

답변:


103

A Looper는 메시지 처리 루프입니다.에서 항목을 읽고 처리합니다 MessageQueue. 이 Looper클래스는 일반적으로 HandlerThread(의 하위 클래스 Thread) 와 함께 사용됩니다 .

A HandlerLooper주로 메시지와 Runnable객체를 스레드의 MessageQueue. Handler가 생성 되면 특정 Looper(및 관련 스레드 및 메시지 대기열)에 바인딩됩니다 .

일반적인 사용에서는를 만들고 시작한 HandlerThread다음 Handler다른 스레드가 HandlerThread인스턴스 와 상호 작용할 수 있는 개체 (또는 개체) 를 만듭니다 . 은 ( Handler는)에서 실행하는 동안 만들어야 HandlerThread하지만 일단 생성되면 Handler의 스케줄링 방법 ( post(Runnable)등)을 사용할 수있는 스레드에 대한 제한이 없습니다 .

Android 애플리케이션의 기본 스레드 (일명 UI 스레드)는 애플리케이션 인스턴스가 생성되기 전에 핸들러 스레드로 설정됩니다.

수업 문서 외에도 여기 에이 모든 것에 대한 좋은 토론이 있습니다 .

추신 위에 언급 된 모든 클래스는 패키지에 android.os있습니다.


@Ted Hopp-Looper의 메시지 대기열이 Thread의 메시지 대기열과 다른가요?
CopsOnRoad

2
@Jack-그들은 같은 것입니다. 안드로이드 에 대한 API 문서MessageQueue A가 그 상태 MessageQueueA는 " 메시지의 목록을 잡고 낮은 수준의 클래스는 파견합니다 Looper. "
테드 Hopp

95

안드로이드의 메인 스레드 가 아닌 스레드에서 UI 구성 요소를 직접 업데이트 하는 것은 불법이라는 것은 널리 알려져 있습니다. 이 안드로이드 문서 ( UI 스레드에서 비용이 많이 드는 작업 처리)비용이 많이 드는 작업을 수행 하고 완료된 후 UI를 업데이트 하기 위해 별도의 스레드를 시작해야하는 경우 따라야 할 단계를 제안합니다 . 아이디어는 메인 스레드 와 관련된 Handler 객체 를 생성하고 적절한 시간에 Runnable 을 게시하는 것입니다. 이것은 메인 스레드 에서 호출됩니다 . 이 메커니즘은 LooperHandler 클래스로 구현됩니다 .Runnable

Looper클래스는 유지 MessageQueue가 목록에 포함, 메시지를 . Looper의 중요한 특징은 그것이 생성 된 스레드연관 되어 있다는 것 입니다 . 이 연결은 영원히 유지 되며 깨지거나 변경할 수 없습니다. 또한 스레드가 있습니다 이상과 연관 될 수없는 일 . 이 연결을 보장하기 위해 스레드 로컬 저장소에 저장되며 생성자를 통해 직접 생성 할 수 없습니다. 이를 만드는 유일한 방법은에서 정적 메서드 준비 를 호출 하는 것입니다 . 준비 메소드는 먼저 ThreadLocal을 검사합니다.LooperLooperLooperLooper스레드와 연관된 Looper가 아직 없는지 확인하기 위해 현재 스레드의. 검사 후 새 파일 Looper이 생성되어 ThreadLocal. 를 준비한 후 루프 메서드를 Looper호출 하여 새 메시지를 확인 하고 처리해야합니다.Handler

이름에서 알 수 Handler있듯이이 클래스는 주로 현재 스레드의 MessageQueue. Handler예는 스레드에 바인딩됩니다. 핸들러 스레드 간의 결합을 통해 달성 Looper하고 MessageQueue. A는 Handler되고 항상에 바인딩Looper 하고, 이후에 바인딩 관련하는 thread 와 함께 Looper. 와 달리 Looper여러 Handler 인스턴스는 동일한 스레드에 바인딩 될 수 있습니다. 에서 post 또는 모든 메서드를 호출 할 때마다 Handler새 메시지가 연결된 MessageQueue. 메시지의 대상 필드는 현재 Handler인스턴스 로 설정됩니다 . 때Looper이 메시지를 수신하면 메시지 의 대상 필드에서 dispatchMessage 를 호출 하여 메시지가 처리 할 Handler 인스턴스로 다시 라우팅되지만 올바른 스레드에 있습니다. Looper, Handler및 간의 관계 MessageQueue는 다음과 같습니다.

여기에 이미지 설명 입력


5
감사! 그러나 핸들러가 먼저 메시지를 메시지 큐에 게시 한 다음 동일한 큐의 메시지 를 처리 하면 요점이 무엇입니까? 메시지를 직접 처리하지 않는 이유는 무엇입니까?
Blake

4
@Blake b / c 한 스레드 (루 퍼가 아닌 스레드)에서 게시하고 있지만 다른 스레드 (루퍼 스레드)에서 메시지를 처리하고 있습니다
numan salati 2013

developer.android.com 에 문서화 된 것보다 훨씬 낫지 만 제공 한 다이어그램에 대한 코드를 보면 좋을 것입니다.
tfmontague

@numansalati-핸들러가 루퍼 스레드에서 메시지를 게시 할 수 없습니까?
CopsOnRoad

78

루퍼부터 시작하겠습니다. Looper가 무엇인지 이해하면 Looper, Handler 및 MessageQueue 간의 관계를 더 쉽게 이해할 수 있습니다. 또한 GUI 프레임 워크의 맥락에서 Looper가 무엇인지 더 잘 이해할 수 있습니다. 루퍼는 2 가지 일을하도록 만들어졌습니다.

1) Looper 는 메소드가 반환 될 때 종료 되는 일반 스레드를 Android 앱이 실행될 때까지 계속 실행 되는 것으로 변환합니다. 이는 GUI 프레임 워크에서 필요 합니다. (기술적으로 는 메소드가 반환 될 때도 종료됩니다 . 이하).run()run()

2) Looper 수행 할 작업이 대기열에 추가되는 대기열을 제공하며 이는 GUI 프레임 워크에서도 필요합니다.

아시다시피, 응용 프로그램이 시작되면 시스템은 "main"이라는 응용 프로그램에 대한 실행 스레드를 생성하고 Android 응용 프로그램은 일반적으로 기본적으로 "main thread"라는 단일 스레드에서 전적으로 실행됩니다. 그러나 메인 스레드는 비밀스럽고 특별한 스레드가 아닙니다 . new Thread()코드로 생성 할 수있는 일반 스레드 일뿐 입니다. 즉, run()메서드가 반환 되면 종료됩니다 ! 아래 예를 생각해보십시오.

public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }

    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}

이제이 간단한 원칙을 Android 앱에 적용 해 보겠습니다. Android 앱이 일반 스레드에서 실행되면 어떻게됩니까? "main"또는 "UI"라는 스레드 또는 응용 프로그램을 시작하고 모든 UI를 그립니다. 따라서 첫 번째 화면이 사용자에게 표시됩니다. 그래서 지금은? 메인 스레드가 종료됩니까? 아니, 안돼. 사용자가 뭔가를 할 때까지 기다려야 하지요? 그러나 우리는 어떻게이 행동을 달성 할 수 있습니까? 글쎄, 우리는 Object.wait()또는Thread.sleep(). 예를 들어, 주 스레드는 첫 번째 화면을 표시하기 위해 초기 작업을 완료하고 휴면합니다. 새 작업을 가져올 때 깨어납니다. 즉, 중단됩니다. 지금까지는 훌륭했지만 지금은 여러 작업을 보관하기 위해 대기열과 같은 데이터 구조가 필요합니다. 사용자가 화면을 연속적으로 터치하고 작업을 완료하는 데 시간이 더 오래 걸리는 경우를 생각해보십시오. 따라서 우리는 선입 선출 방식으로 수행 할 작업을 보관할 데이터 구조가 필요합니다. 또한 인터럽트를 사용하여 계속 실행되고 처리되는 작업 도착시 스레드를 구현하는 것은 쉽지 않으며 복잡하고 유지 관리가 불가능한 코드로 이어집니다. 우리는 오히려 그러한 목적을 위해 새로운 메커니즘을 만들고 싶습니다 . 이것이 바로 Looper에 관한 것 입니다. Looper 클래스공식 문서"스레드에는 기본적으로 연관된 메시지 루프가 없습니다"라고 말하며 Looper는 "스레드에 대한 메시지 루프를 실행하는 데 사용되는"클래스입니다. 이제 그 의미를 이해할 수 있습니다.

Handler 및 MessageQueue로 이동하겠습니다. 먼저 MessageQueue는 위에서 언급 한 대기열입니다. 그것은 루퍼 안에 있으며 그게 다입니다. Looper 클래스의 소스 코드로 확인할 수 있습니다 . Looper 클래스에는 MessageQueue의 멤버 변수가 있습니다.

그럼 핸들러 란? 대기열이 있으면 새 작업을 대기열에 넣을 수있는 방법이 있어야합니다. 그렇죠? 그것이 핸들러가하는 일입니다. 다양한 post(Runnable r)방법을 사용하여 새로운 작업을 대기열 (MessageQueue)에 넣을 수 있습니다 . 그게 다야. 이것은 Looper, Handler 및 MessageQueue에 관한 것입니다.

마지막 말은 기본적으로 Looper는 GUI 프레임 워크에서 발생하는 문제를 해결하기 위해 만들어진 클래스입니다. 그러나 이러한 종류의 요구는 다른 상황에서도 발생할 수 있습니다. 실제로 이것은 다중 스레드 애플리케이션에 대해 꽤 유명한 패턴이며 Doug Lea의 "자바에서 동시 프로그래밍"(특히 4.1.4 장 "작업자 스레드"가 도움이 될 것입니다)에서 더 많은 것을 배울 수 있습니다. 또한 이러한 종류의 메커니즘이 Android 프레임 워크에서 고유하지 않다고 생각할 수 있지만 모든 GUI 프레임 워크에는 이와 비슷한 것이 필요할 수 있습니다. Java Swing 프레임 워크에서 거의 동일한 메커니즘을 찾을 수 있습니다.


4
최고의 답변입니다. 이 자세한 설명에서 자세히 알아보십시오. 좀 더 자세히 설명하는 블로그 게시물이 있는지 궁금합니다.
capt.swag

핸들러를 사용하지 않고 메시지를 MessageQueue에 추가 할 수 있습니까?
CopsOnRoad

@CopsOnRoad 아니요 직접 추가 할 수 없습니다.
Faisal Naseer

내 하루를 만들었습니다 ... 당신에게 많은 사랑을 :)
Rahul Matte

26

MessageQueue:에서 디스패치 할 메시지 목록을 포함하는 하위 수준 클래스 Looper입니다. 메시지는에 직접 추가되는 MessageQueue것이 아니라 . [ 3 ] Handler과 연결된 개체를 통해 추가됩니다 Looper.

Looper: MessageQueue디스패치 할 메시지가 포함 된을 반복 합니다. 큐를 관리하는 실제 작업은 Handler메시지 큐에서 메시지를 처리 ​​(추가, 제거, 발송)하는 담당자 가 수행합니다 . [ 2 ]

Handler: 그것은 당신이 보내 처리 할 수 MessageRunnable스레드의와 관련된 객체 MessageQueue. 각 Handler 인스턴스는 단일 스레드 및 해당 스레드의 메시지 큐와 연결됩니다. [ 4 ]

새를 만들 때 Handler, 그것은 그것을 만드는 스레드의 스레드 / 메시지 큐에 바인딩 - 그 시점에서 이 해당 메시지 큐에 메시지와보다 Runnable을 제공 할 것입니다이를 실행 그들이 메시지 큐 나올으로 .

이해를 돕기 위해 아래 이미지 [ 2 ]를 참조하십시오.

여기에 이미지 설명 입력


0

답변 확장, @K_Anas, 예를 들어 설명했듯이

안드로이드의 메인 스레드가 아닌 스레드에서 UI 구성 요소를 직접 업데이트하는 것은 불법이라는 것은 널리 알려져 있습니다.

예를 들어 Thread를 사용하여 UI를 업데이트하려는 경우.

    int count = 0;
    new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                while(true) {
                    sleep(1000);
                    count++;
                    textView.setText(String.valueOf(count));
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


   ).start();

앱이 예외와 함께 충돌합니다.

android.view.ViewRoot $ CalledFromWrongThreadException : 뷰 계층 구조를 생성 한 원래 스레드 만 뷰를 터치 할 수 있습니다.

즉 당신은 사용할 필요가 Handler받는 참조를 유지하는 MainLooper 즉, Main ThreadUI Thread하고 같이 작업을 전달합니다 Runnable.

  Handler handler = new Handler(getApplicationContext().getMainLooper);
        int count = 0;
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {
                    while(true) {
                        sleep(1000);
                        count++;
                        handler.post(new Runnable() {
                           @Override
                           public void run() {
                                 textView.setText(String.valueOf(count));
                           }
                         });

                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    ).start() ;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.