Android 메시지 메커니즘 상세 설명 및 인 스 턴 스 코드
1.개술
Android 애플 리 케 이 션 이 시 작 될 때 기본 값 으로 메 인 스 레 드(UI 스 레 드)가 있 습 니 다.이 스 레 드 에 서 는 메시지 큐(Message Queue)가 연 결 됩 니 다.모든 작업 이 메시지 큐 로 봉 인 된 후 메 인 스 레 드 에 전 달 됩 니 다.메 인 스 레 드 가 종료 되 지 않도록 메시지 큐 의 작업 을 죽은 순환 에 두 고 프로그램 은 죽은 순환 을 계속 실행 하 는 것 과 같 습 니 다.순환 할 때마다 내부 메시지 큐 에서 메 시 지 를 꺼 낸 다음 에 해당 하 는 메시지 처리 함수(handlerMessage)를 되 돌려 서 메 시 지 를 완성 한 후에 계속 순환 합 니 다.메시지 큐 가 비어 있 으 면.스 레 드 는 기다 림 을 막 습 니 다.그래서 물 러 나 지 않 는 다.다음 그림 에서 보 듯 이:
Handler,Looper,Message 는 무슨 관계 가 있 습 니까?
하위 스 레 드 에서 시간 이 걸 리 는 작업 을 수행 하려 면 UI 를 업데이트 해 야 하 는 경우 가 많 습 니 다.가장 많이 사용 되 는 것 은 Handler 를 통 해 하나의 메 시 지 를 UI 스 레 드 에 포스트 한 다음 Handler 의 handlerMessage 방법 에서 처리 하 는 것 입 니 다.그리고 모든 Handler 는 하나의 메시지 큐(Message Queue)와 연 결 됩 니 다.Looper 는 Message Queue 를 만 드 는 것 을 담당 하고 모든 Looper 는 하나의 스 레 드(Looper 는 ThreadLocal 을 통 해 패키지)와 연 결 됩 니 다.기본 적 인 상황 에서 Message Queue 는 메 인 스 레 드 의 메시지 큐 만 있 습 니 다.
위 는 안 드 로 이 드 메시지 메커니즘 의 기본 원리 입 니 다.더 자세히 알 고 싶다 면 소스 코드 부터 보 겠 습 니 다.
2.소스 코드 판독
(1)Activity Thread 메 인 스 레 드 에서 시작 메시지 순환 Looper 시작
public final class ActivityThread {
public static void main(String[] args) {
//
//1. Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
//2.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
Activity Thread 는 Looper.prepareMainLooper()를 통 해 메 인 스 레 드 의 메시지 큐 를 만 들 고 마지막 으로 Looper.loop()을 실행 하여 메시지 큐 를 시작 합 니 다.Handler 관련 메시지 큐 와 스 레 드.(2)Handler 관련 메시지 큐 와 스 레 드
public Handler(Callback callback, boolean async) {
//
// Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//
mQueue = mLooper.mQueue;
}
Handler 는 내부 에서 Looper.getLooper()방법 을 통 해 Looper 대상 을 가 져 오고 이와 관련 하여 메시지 큐 를 가 져 옵 니 다.그럼 Looper.getLooper()는 어떻게 일 합 니까?
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
public static void prepare() {
prepare(true);
}
// Looper
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
// UI Looper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
Looper 클래스 에서 my Looper()방법 은 sThreadLocal.get()을 통 해 얻 을 수 있 습 니 다.prepareMainLooper()에서 prepare()방법 을 호출 하여 이 방법 에서 Looper 대상 을 만 들 고 대상 을 sThreadLocal()로 설정 합 니 다.이렇게 대열 은 스 레 드 와 연결 되 었 다.sThreadLocal.get()방법 을 통 해 서로 다른 스 레 드 가 상대방 의 메시지 대기 열 에 접근 할 수 없 도록 합 니 다.UI 를 업데이트 하 는 Handler 를 메 인 스 레 드 에 만들어 야 하 는 이 유 는 무엇 입 니까?
Handler 가 메 인 스 레 드 의 메시지 큐 와 연결 되 어야 handlerMessage 가 UI 스 레 드 에서 실 행 됩 니 다.이때 UI 스 레 드 가 안전 합 니 다.
(3)메시지 순환,메시지 처리
메시지 순환 의 구축 은 Looper.loop()방법 을 통 해 이 루어 집 니 다.원본 코드 는 다음 과 같 습 니 다:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//1.
final MessageQueue queue = me.mQueue;
//2. ,
for (;;) {
//3. ,
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//4.
msg.target.dispatchMessage(msg);
//
msg.recycleUnchecked();
}
}
상기 프로그램 에서 알 수 있 듯 이 loop()방법 은 실질 적 으로 하나의 순환 을 구축 한 다음 에 메시지 대기 열 에서 하나씩 정 보 를 꺼 내 서 마지막 으로 정 보 를 처리 하 는 것 이다.Looper:Looper.prepare()를 통 해 Looper 대상(메시지 대기 열 은 Looper 대상 에 봉 인 됨)을 만 들 고 sThreadLocal 에 저장 한 다음 Looper.loop()을 통 해 메시지 순환 을 합 니 다.이 두 단 계 는 보통 쌍 을 이 루어 나타 납 니 다.
public final class Message implements Parcelable {
//target
Handler target;
//Runnable callback
Runnable callback;
// ,
Message next;
}
원본 코드 에서 알 수 있 듯 이 target 은 Handler 형식 이다.실제로 한 바퀴 돌 고 Handler 를 통 해 메시지 큐 에 메 시 지 를 보 내 고 메시지 큐 는 Handler 에 메 시 지 를 보 내 처리 했다.Handle 클래스 에서:
// ,
public void handleMessage(Message msg) {
}
private static void handleCallback(Message message) {
message.callback.run();
}
//
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
위 프로그램 에서 알 수 있 듯 이 dispatchMessage 는 배포 방법 일 뿐 입 니 다.Run nable 형식의 callback 이 비어 있 으 면 handleMessage 를 실행 하여 메 시 지 를 처리 합 니 다.이 방법 은 비어 있 습 니 다.UI 를 업데이트 하 는 코드 를 이 함수 에 쓰 겠 습 니 다.콜백 이 비어 있 지 않 으 면 handle Callback 을 실행 합 니 다.이 방법 은 콜백 의 run 방법 을 호출 합 니 다.사실 이것 은 Handler 가 나 눠 주 는 두 가지 유형 입 니 다.예 를 들 어 post(Runnable callback)는 callback 이 비어 있 지 않 습 니 다.Handler 를 사용 하여 sendmessage 를 사용 할 때 callback 을 설정 하지 않 기 때문에 handlerMessage 를 실행 합 니 다.
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public String getMessageName(Message message) {
if (message.callback != null) {
return message.callback.getClass().getName();
}
return "0x" + Integer.toHexString(message.what);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
위 프로그램 에서 볼 수 있 듯 이 post(Runnable r)에 서 는 Runnable 을 Message 대상 으로 포장 하고 Runnable 대상 을 Message 대상 의 callback 에 설정 하 며 마지막 으로 이 대상 을 메시지 큐 에 삽입 합 니 다.sendmessage 도 비슷 한 실현:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
post 하나의 Runnable 이 든 Message 든 sendmessage Delayed(msg,time)방법 을 호출 합 니 다.Handler 는 최종 적 으로 Message Queue 에 메 시 지 를 추가 하고 Looper 는 Message Queue 에서 메 시 지 를 계속 읽 고 Handler 의 dispatchMessage 를 호출 하여 메 시 지 를 전달 합 니 다.그러면 메시지 가 끊임없이 생 성 되 고 Message Queue 에 추가 되 며 Handler 에 의 해 처리 되 고 Android 응용 프로그램 이 작 동 합 니 다.3.검사
new Thread(){
Handler handler = null;
public void run () {
handler = new Handler();
};
}.start();
상술 한 코드 에 문제 가 있 습 니까?Looper 대상 은 ThreadLocal 입 니 다.즉,모든 스 레 드 는 자신의 Looper 를 사용 합 니 다.이 Looper 는 비어 있 을 수 있 습 니 다.그러나 하위 스 레 드 에 Handler 대상 을 만 들 때 Looper 가 비어 있 으 면 이상 이 발생 합 니 다.
public Handler(Callback callback, boolean async) {
//
// Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//
mQueue = mLooper.mQueue;
}
mLooper 가 비어 있 을 때 이상 을 던 집 니 다.Looper 대상 이 생 성 되 지 않 았 기 때문에 sThreadLocal.get()은 null 로 돌아 갑 니 다.Handler 의 기본 원 리 는 Message Queue 와 연 계 를 맺 고 Message Queue 에 메 시 지 를 전달 하 는 것 이다.Message Queue 가 없 으 면 Handler 가 존재 할 필요 가 없고 Message Queue 가 Looper 에 갇 혀 있 기 때문에 Handler 를 만 들 때 Looper 가 비어 있 으 면 안 된다.해결 방법 은 다음 과 같다.
new Thread(){
Handler handler = null;
public void run () {
// Looper, ThreadLocal
Looper.prepare()
handler = new Handler();
//
Looper.loop();
};
}.start();
Looper 만 만 들 고 메시지 순환 을 시작 하지 않 으 면 이상 을 던 지지 않 지만 handler 를 통 해 post 나 sendmessage()도 유효 하지 않 습 니 다.메시지 가 메시지 대기 열 에 추 가 될 수 있 지만 메시지 순환 이 시작 되 지 않 아 메시지 대기 열 에서 메 시 지 를 가 져 오고 실행 되 지 않 기 때문이다.읽 어 주 셔 서 감사합니다. 여러분 에 게 도움 이 되 기 를 바 랍 니 다.본 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.