Looper
, Handler
및에 대한 공식 Android 문서 / 가이드를 확인했습니다 MessageQueue
. 그러나 나는 그것을 얻을 수 없었다. 나는 안드로이드를 처음 접했고 이러한 개념과 매우 혼동되었습니다.
답변:
A Looper
는 메시지 처리 루프입니다.에서 항목을 읽고 처리합니다 MessageQueue
. 이 Looper
클래스는 일반적으로 HandlerThread
(의 하위 클래스 Thread
) 와 함께 사용됩니다 .
A Handler
는 Looper
주로 메시지와 Runnable
객체를 스레드의 MessageQueue
. Handler
가 생성 되면 특정 Looper
(및 관련 스레드 및 메시지 대기열)에 바인딩됩니다 .
일반적인 사용에서는를 만들고 시작한 HandlerThread
다음 Handler
다른 스레드가 HandlerThread
인스턴스 와 상호 작용할 수 있는 개체 (또는 개체) 를 만듭니다 . 은 ( Handler
는)에서 실행하는 동안 만들어야 HandlerThread
하지만 일단 생성되면 Handler
의 스케줄링 방법 ( post(Runnable)
등)을 사용할 수있는 스레드에 대한 제한이 없습니다 .
Android 애플리케이션의 기본 스레드 (일명 UI 스레드)는 애플리케이션 인스턴스가 생성되기 전에 핸들러 스레드로 설정됩니다.
수업 문서 외에도 여기 에이 모든 것에 대한 좋은 토론이 있습니다 .
추신 위에 언급 된 모든 클래스는 패키지에 android.os
있습니다.
MessageQueue
A가 그 상태 MessageQueue
A는 " 메시지의 목록을 잡고 낮은 수준의 클래스는 파견합니다 Looper
. "
안드로이드의 메인 스레드 가 아닌 스레드에서 UI 구성 요소를 직접 업데이트 하는 것은 불법이라는 것은 널리 알려져 있습니다. 이 안드로이드 문서 ( UI 스레드에서 비용이 많이 드는 작업 처리) 는 비용이 많이 드는 작업을 수행 하고 완료된 후 UI를 업데이트 하기 위해 별도의 스레드를 시작해야하는 경우 따라야 할 단계를 제안합니다 . 아이디어는 메인 스레드 와 관련된 Handler 객체 를 생성하고 적절한 시간에 Runnable 을 게시하는 것입니다. 이것은 메인 스레드 에서 호출됩니다 . 이 메커니즘은 Looper 및 Handler 클래스로 구현됩니다 .Runnable
Looper
클래스는 유지 MessageQueue가 목록에 포함, 메시지를 . Looper의 중요한 특징은 그것이 생성 된 스레드 와 연관 되어 있다는 것 입니다 . 이 연결은 영원히 유지 되며 깨지거나 변경할 수 없습니다. 또한 스레드가 있습니다 이상과 연관 될 수없는 일 . 이 연결을 보장하기 위해 스레드 로컬 저장소에 저장되며 생성자를 통해 직접 생성 할 수 없습니다. 이를 만드는 유일한 방법은에서 정적 메서드 준비 를 호출 하는 것입니다 . 준비 메소드는 먼저 ThreadLocal을 검사합니다.Looper
Looper
Looper
Looper
스레드와 연관된 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
는 다음과 같습니다.
루퍼부터 시작하겠습니다. 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 프레임 워크에서 거의 동일한 메커니즘을 찾을 수 있습니다.
MessageQueue
:에서 디스패치 할 메시지 목록을 포함하는 하위 수준 클래스 Looper
입니다. 메시지는에 직접 추가되는 MessageQueue
것이 아니라 . [ 3 ] Handler
과 연결된 개체를 통해 추가됩니다 Looper
.
Looper
: MessageQueue
디스패치 할 메시지가 포함 된을 반복 합니다. 큐를 관리하는 실제 작업은 Handler
메시지 큐에서 메시지를 처리 (추가, 제거, 발송)하는 담당자 가 수행합니다 . [ 2 ]
Handler
: 그것은 당신이 보내 처리 할 수 Message
및 Runnable
스레드의와 관련된 객체 MessageQueue
. 각 Handler 인스턴스는 단일 스레드 및 해당 스레드의 메시지 큐와 연결됩니다. [ 4 ]
새를 만들 때 Handler
, 그것은 그것을 만드는 스레드의 스레드 / 메시지 큐에 바인딩 - 그 시점에서 이 해당 메시지 큐에 메시지와보다 Runnable을 제공 할 것입니다 및 이를 실행 그들이 메시지 큐 나올으로 .
이해를 돕기 위해 아래 이미지 [ 2 ]를 참조하십시오.
답변 확장, @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 Thread
나 UI 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() ;