Android 메시지 메커니즘 상세 설명 및 인 스 턴 스 코드

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()도 유효 하지 않 습 니 다.메시지 가 메시지 대기 열 에 추 가 될 수 있 지만 메시지 순환 이 시작 되 지 않 아 메시지 대기 열 에서 메 시 지 를 가 져 오고 실행 되 지 않 기 때문이다.
읽 어 주 셔 서 감사합니다. 여러분 에 게 도움 이 되 기 를 바 랍 니 다.본 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

좋은 웹페이지 즐겨찾기