Handler 메시지 메커니즘 해석

7382 단어
Handler 메시지 메커니즘 해석
Android는 UI를 업데이트하는 일련의 메커니즘을 제공하며 메시지를 보내고 처리할 수 있는 메시징 메커니즘을 제공합니다.
Handler
Handler 대상이 Looper와 소통하여push의 새로운 소식을 MessageQueue에 전달하도록 합니다.또는 Message Queue에서 Looper가 체크 아웃한 메시지를 받습니다.
메서드
  • 구조 방법
     public Handler(Callback callback, boolean async) {
         if (FIND_POTENTIAL_LEAKS) {
             final Class extends Handler> klass = getClass();
             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                 (klass.getModifiers() & Modifier.STATIC) == 0) {
             Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                 klass.getCanonicalName());
             }
         }
    
         mLooper = Looper.myLooper();
         if (mLooper == null) {
             throw new RuntimeException(
             "Can't create handler inside thread that has not called Looper.prepare()");
         }
         mQueue = mLooper.mQueue;
         mCallback = callback;
         mAsynchronous = async;
     }
    
  • 몇 가지 구조 방법은 최종적으로 이 구조 방법을 호출할 것이다myLooper().현재 Looper 대상을 가져오고 looper를 통해MessageQueue를 가져오며handler와 looper의 연결을 완료합니다
  • 메시지 발송 방법
     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);
     }
    
  • 대기열이 null 이면 이상을 던지고, 그렇지 않으면 대기열에 메시지를 넣습니다.
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    메시지를 넣을 때 목표를 지정하고 기본적으로 자신을 보냅니다.
    메시지를 대기열에 넣으면 Looper가 looper()를 통해 메시지 대기열을 처리합니다.
  • 메시지 배포 방법
      /**
      * Handle system messages here.
      */
     public void dispatchMessage(Message msg) {
         if (msg.callback != null) {
             handleCallback(msg);
         } else {
             if (mCallback != null) {
                 if (mCallback.handleMessage(msg)) {
                     return;
                 }
             }
             handleMessage(msg);
         }
     }
    
  • 이 방법에서 메시지 처리 handleCallback 방법을 호출합니다
    내부 커넥터Callback
    public interface Callback {
        public boolean handleMessage(Message msg);
    }
    

    Looper
    이 스레드의 MessageQueue (메시지 대기열) 를 관리하기 위해 로퍼 대상만 생성할 수 있습니다.
  • 내부에 메시지 대기열MessageQueue이 포함되어 있으며 모든handler가 보낸 메시지가 이 대기열을 통과합니다.
  • Looper.Looper 방법은 사순환으로 끊임없이 MessageQueue에서Message를 추출하고 있으면 처리하고 없으면 막는다
  • Looper 클래스의 방법
  • Looper.prepare () 방법 (prepare (true)
     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));
     }
    
  • 이 방법은 새 Looper 를 만듭니다. 한 라인에 로퍼가 있으면 오류가 발생하고, 없으면 개인 구조 방법으로 새 Looper 를 만듭니다.
  • 사유의 구조 방법
         private Looper(boolean quitAllowed) {
             mQueue = new MessageQueue(quitAllowed);
             mThread = Thread.currentThread();
         } 
    
  • 생성 MessageQueue 및 현재 연결 Thread
  • myLopper()
     /**
      * Return the Looper object associated with the current thread.  Returns
      * null if the calling thread is not associated with a Looper.
      */
     public static @Nullable Looper myLooper() {
         return sThreadLocal.get();
     }
    

  • 현재 Looper 객체를 가져옵니다.
  • myQueue()
     /**
      * Return the {@link MessageQueue} object associated with the current
      * thread.  This must be called from a thread running a Looper, or a
      * NullPointerException will be thrown.
      */
     public static @NonNull MessageQueue myQueue() {
         return myLooper().mQueue;
     }
    

  • Looper에서 MessageQueue 가져오기
  • 사순환방법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.");
         }
         final MessageQueue queue = me.mQueue;
    
         // Make sure the identity of this thread is that of the local process,
         // and keep track of what that identity token actually is.
         Binder.clearCallingIdentity();
         final long ident = Binder.clearCallingIdentity();
    
     for (;;) {
         Message msg = queue.next(); // might block
         if (msg == null) {
             // No message indicates that the message queue is quitting.
             return;
         }
    
         // This must be in a local variable, in case a UI event sets the logger
         Printer logging = me.mLogging;
         if (logging != null) {
             logging.println(">>>>> Dispatching to " + msg.target + " " +
                     msg.callback + ": " + msg.what);
         }
    
         msg.target.dispatchMessage(msg);
    
         if (logging != null) {
             logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
         }
    
         // Make sure that during the course of dispatching the
         // identity of the thread wasn't corrupted.
         final long newIdent = Binder.clearCallingIdentity();
         if (ident != newIdent) {
             Log.wtf(TAG, "Thread identity changed from 0x"
                     + Long.toHexString(ident) + " to 0x"
                     + Long.toHexString(newIdent) + " while dispatching to "
                     + msg.target.getClass().getName() + " "
                     + msg.callback + " what=" + msg.what);
         }
    
         msg.recycleUnchecked();
         }
     }
    
  • 이 방법은 대기열의 메시지를 처리하는 데 사용됩니다: myLooper () 방법으로 앞의 Looper 를 가져와서 현재 메시지 대기열을 가져오고, 메시지 대기열의 next() 방법으로 메시지를 가져옵니다.null이 아닐 때handler의 dispatchMessage(msg) 방법을 사용합니다.
    MessageQueue 메시지 대기열
    메시지 저장 컨테이너
    ThreadLocal class
    역할: 라인에 정보 변수를 저장합니다.thread와 looper 사이의 관계를 책임진다
    메서드
  • set
     public void set(T value) {
         Thread currentThread = Thread.currentThread();
         Values values = values(currentThread);
         if (values == null) {
             values = initializeValues(currentThread);
         }
             values.put(this, value);
     }
    

  • 키 값 쌍의 형식은 Thread와 Looper 간의 관계를 저장하고 Thread는 키이고 Looper는 값이다
  • get
     public T get() {
         // Optimized for the fast path.
         Thread currentThread = Thread.currentThread();
         Values values = values(currentThread);
         if (values != null) {
             Object[] table = values.table;
             int index = hash & values.mask;
             if (this.reference == table[index]) {
                 return (T) table[index + 1];
             }
         } else {
             values = initializeValues(currentThread);
         }
    
         return (T) values.getAfterMiss(this);
     }
    

  • 현재 라인에 대응하는 looper를 꺼냅니다.
    내부 클래스 Values

    좋은 웹페이지 즐겨찾기