android 소스 분석 -- MessageQueue

오전에 핸들러의 원본 코드를 대충 보았는데, 관련 클래스인 looper 클래스와 MessageQueue 클래스도 함께 보았다.   
먼저 Messagequeue를 보십시오. 먼저 클래스 소개:
Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through MessageQueue.IdleHandler objects associated with the Looper.

You can retrieve the MessageQueue for the current thread with Looper.myQueue().

 
메시지 목록을 저장하는 저급 클래스, 메시지는
Looper
대상자 파견.메시지가 직접 추가되는 것은 아닙니다.
MessageQueue
에서
Looper
객체 연관
MessageQueue.IdleHandler
객체가 추가됩니다.
Looper를 호출합니다.myQueue 메서드는 현재 스레드의 MessageQueue를 가져옵니다.
클래스의 변수를 살펴보겠습니다.
Message mMessages;
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuiting;
    boolean mQuitAllowed = true;

    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
    private boolean mBlocked;

    @SuppressWarnings("unused")
    private int mPtr; // used by native code
    
    private native void nativeInit();
    private native void nativeDestroy();
    private native void nativePollOnce(int ptr, int timeoutMillis);
    private native void nativeWake(int ptr);
  • 메시지의 실례
  • IdleHandler 객체의 ArrayList 를 저장합니다
  • IdleHandler 배열
  • boolean 값으로 Thread가 종료되었는지 판단합니다
  • 로그아웃 허용 여부를 판단하는 boolean 값
  • boolean 값으로 next()가 0이 아닌 pollOnce()를 기다리는 것을 막는지 판단합니다
  • 다음은 네이티브 코드입니다.본문은 상세한 설명을 하지 않는다

  • 앞에서 언급한 메세지는 메세지Queue에 직접 추가되는 것이 아니라 IdleHandler 인터페이스를 통해 메세지를 추가합니다. 그래서 IdleHandler를 먼저 보십시오.
    /**
         * Callback interface for discovering when a thread is going to block
         * waiting for more messages.
         */
        public static interface IdleHandler {
            /**
             * Called when the message queue has run out of messages and will now
             * wait for more.  Return true to keep your idle handler active, false
             * to have it removed.  This may be called if there are still messages
             * pending in the queue, but they are all scheduled to be dispatched
             * after the current time.
             */
            boolean queueIdle();
        }

    더 많은 메시지가 막힐 때를 기다리는 데 사용되는 인터페이스메시지 대기열이 비어 있을 때, 더 많은 메시지를 기다릴 때, 이queueIdle 함수를 실행합니다. 되돌아오면 idlehandler의 활성 상태를 유지하고, 되돌아오는 값이false라면, 프로그램에서 이 IdleHandler를 제거합니다. 그렇지 않으면 프로그램에서 이 IdleHandler를 계속 유지합니다. 다음 한가할 때 이 IdleHandler를 다시 집결합니다.그러나 메시지 대기열에 메시지가 있으면, 실행 시간이 현재 시간 이후에queueIdle도 호출됩니다.
    코드를 내려다봅니다.
    /**
         * Add a new {@link IdleHandler} to this message queue.  This may be
         * removed automatically for you by returning false from
         * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
         * invoked, or explicitly removing it with {@link #removeIdleHandler}.
         * 
         * <p>This method is safe to call from any thread.
         * 
         * @param handler The IdleHandler to be added.
         */
        public final void addIdleHandler(IdleHandler handler) {
            if (handler == null) {
                throw new NullPointerException("Can't add a null IdleHandler");
            }
            synchronized (this) {
                mIdleHandlers.add(handler);
            }
        }

    메시지 대기열에 새 MessageQueue를 추가합니다.IdleHandler.IdleHandler를 호출합니다.queueIdle () 이 false 를 반환할 때 이 MessageQueue.IdleHandler는 자동으로 메시지 대기열에서 제거됩니다.또는 removeIdleHandler(MessageQueue.IdleHandler)를 호출하면 메시지 대기열에서 MessageQueue를 제거할 수 있습니다.IdleHandler.
    이 방법은 라인이 안전하다.
    추가가 있으면 삭제:
    /**
         * Remove an {@link IdleHandler} from the queue that was previously added
         * with {@link #addIdleHandler}.  If the given object is not currently
         * in the idle list, nothing is done.
         * 
         * @param handler The IdleHandler to be removed.
         */
        public final void removeIdleHandler(IdleHandler handler) {
            synchronized (this) {
                mIdleHandlers.remove(handler);
            }
        }

       
    대기열에서 이전에 addIdlehandler(MessageQueue.IdleHandler)를 호출하여 추가한 MessageQueue를 제거합니다.IdleHandler.만약 Handler가 현재 빈 목록에 없다면, 아무것도 하지 않습니다.
    구조 방법을 살펴보겠습니다.
    MessageQueue() {
            nativeInit();
        }

    native 코드를 호출하여 초기화를 완료합니다.
    클래스 제거를 다시 썼습니다.
    @Override
        protected void finalize() throws Throwable {
            try {
                nativeDestroy();
            } finally {
                super.finalize();
            }
        }

    native 코드를 호출하여 클래스를 삭제합니다.
    다음은 다음 메시지 가져오기 방법입니다.
    final Message next() {
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            int nextPollTimeoutMillis = 0;
    
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
                nativePollOnce(mPtr, nextPollTimeoutMillis);
    
                synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    final long now = SystemClock.uptimeMillis();
                    final Message msg = mMessages;
                    if (msg != null) {
                        final long when = msg.when;
                        if (now >= when) {
                            mBlocked = false;
                            mMessages = msg.next;
                            msg.next = null;
                            if (false) Log.v("MessageQueue", "Returning message: " + msg);
                            msg.markInUse();
                            return msg;
                        } else {
                            nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                        }
                    } else {
                        nextPollTimeoutMillis = -1;
                    }
    
                    // If first time, then get the number of idlers to run.
                    if (pendingIdleHandlerCount < 0) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    if (pendingIdleHandlerCount == 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }
    
                    if (mPendingIdleHandlers == null) {
                        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                    }
                    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
                }
    
                // Run the idle handlers.
                // We only ever reach this code block during the first iteration.
                for (int i = 0; i < pendingIdleHandlerCount; i++) {
                    final IdleHandler idler = mPendingIdleHandlers[i];
                    mPendingIdleHandlers[i] = null; // release the reference to the handler
    
                    boolean keep = false;
                    try {
                        keep = idler.queueIdle();
                    } catch (Throwable t) {
                        Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                    }
    
                    if (!keep) {
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }
    
                // Reset the idle handler count to 0 so we do not run them again.
                pendingIdleHandlerCount = 0;
    
                // While calling an idle handler, a new message could have been delivered
                // so go back and look again for a pending message without waiting.
                nextPollTimeoutMillis = 0;
            }
        }

    이것은 메시지를 추출하여 처리할 때 메시지Queue에 있는 메시지가 시간 요구에 부합되는지 판단하여 메시지를 추출하여 처리해야 하는지 여부를 결정합니다. 이런 방식으로 메시지를 정시 처리합니다.그래서 이 함수를 호출할 때 라인을 대기 상태로 들어갈 수 있습니다.어떤 상황에서 노선이 대기 상태에 들어갈까요?두 가지 상황, 첫째, 메시지 대기열에 메시지가 없을 때, 그것은 라인을 대기 상태로 진입시킨다.둘째, 메시지 대기열에 메시지가 있지만 메시지가 실행되는 시간을 지정합니다. 아직 이 시간이 되지 않았습니다. 라인도 대기 상태로 들어갑니다.메시지 대기열의 메시지는 시간의 선후에 따라 정렬된다.
    먼저 두 개의 매개 변수의 뜻을 보십시오. pending Idle Handler Count: 빈 Handler 개수입니다.처음 순환할 때만 값이 -1이다.nextPollTimeoutMillis: 다음 퀴즈 시간을 이해합니다. 현재 메시지 대기열에 메시지가 없으면 기다릴 때, for 순환이 시작될 때, 전송된 값은 0입니다. 기다리지 않음을 나타냅니다.
    for 순환 내부:
     if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }

    nextPollTimeoutMillis 값이 0이 아니면 메시지를 기다립니다.다음 문장은 현재 메시지 대기열에 메시지가 있는지 확인하는 것입니다.
     nativePollOnce(mPtr, nextPollTimeoutMillis);

    nativePollOnce가 돌아오면 next 함수는 mMessages에서 메시지를 추출합니다.즉, native Poll Once를 되돌려보내려면 최소한 메시지 대기열에 메시지를 추가해야 한다. 그렇지 않으면native Poll Once는 한 번의 헛수고에 불과하다.만약nativePollOnce가 Native층에서 기다린다면 Native층도 메시지를 보낼 수 있다는 것을 나타낸다. (메시지, 업계의 습관에 적응하기 위해 이 책은 영어를 사용하고 필요할 때 중국어를 사용한다. 다른 단어는 이와 같다.) 그러나 Message류의 실현 코드를 보면 이 클래스와 Native층은 아무런 관계가 없다. (즉, Native층은 자바층의 Message 대상을 구성하여 자바층의 Message 대기열에 삽입할 수 없다.)그럼 네이티브 폴 원스는 무엇을 기다리고 있을까요?위의 문제에 대해 일부 독자들의 마음속에 이미 답이 있다고 믿는다. 네이티브 폴 원스는 자바 층에서 오는 메시지를 기다리는 것 외에 네이티브 층에서 많은 일을 했다.
    그리고 synchronized 내부 코드를 보십시오.
    synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    final long now = SystemClock.uptimeMillis();
                    final Message msg = mMessages;
                    if (msg != null) {
                        final long when = msg.when;
                        if (now >= when) {
                            mBlocked = false;
                            mMessages = msg.next;
                            msg.next = null;
                            if (false) Log.v("MessageQueue", "Returning message: " + msg);
                            msg.markInUse();
                            return msg;
                        } else {
                            nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                        }
                    } else {
                        nextPollTimeoutMillis = -1;
                    }
    
                    // If first time, then get the number of idlers to run.
                    if (pendingIdleHandlerCount < 0) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    if (pendingIdleHandlerCount == 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }
    
                    if (mPendingIdleHandlers == null) {
                        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                    }
                    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
                }

    다음 메시지를 검색해 보고 존재하면 되돌려줍니다.만약 메시지가 비어 있다면, nextPollTimeoutMillis는 -1의 값을 부여합니다.다음 소식을 기다려야 합니다.만약 비어 있지 않다면, 시간이 이 메시지를 처리할 수 있는지 판단하고, 조건에 부합된다면, 메시지를 looper에 전송합니다.그렇지 않으면 대기 시간을 계산하고 이 시간까지 기다린 다음 실행합니다
    // If first time, then get the number of idlers to run.
                    if (pendingIdleHandlerCount < 0) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    if (pendingIdleHandlerCount == 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }

    만약 처음이라면, 유휴 idlerhandler를 받아서 실행합니다.유휴된idlehandler가 없으면 막고 더 많은 메시지를 계속 순환해서 기다립니다.
    if (mPendingIdleHandlers == null) {
                        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                    }
                    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);

    mIdleHandlers에 등록된 IdleHandler를 꺼내서 mPendingIdleHandlers 수조에 놓으세요.
        
    다음은 등록된 IdleHander입니다.
    // Run the idle handlers.
                // We only ever reach this code block during the first iteration.
                for (int i = 0; i < pendingIdleHandlerCount; i++) {
                    final IdleHandler idler = mPendingIdleHandlers[i];
                    mPendingIdleHandlers[i] = null; // release the reference to the handler
    
                    boolean keep = false;
                    try {
                        keep = idler.queueIdle();
                    } catch (Throwable t) {
                        Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                    }
    
                    if (!keep) {
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }

    유휴 프로세서를 실행합니다.우리는 단지 이 코드 블록이 처음으로 교체될 수 있을 뿐이다.이 IdleHandler를 실행한 후에 다음에 네이티브 PollOnce 함수를 호출할 때 시간 초과 시간을 설정하지 않습니다. 왜냐하면 IdleHandler를 실행할 때 새로운 메시지가 메시지 대기열에 추가되었을 가능성이 높기 때문입니다. 따라서nextPollTimeoutMillis의 값을 초기화해야 합니다.
     // Reset the idle handler count to 0 so we do not run them again.
                pendingIdleHandlerCount = 0;
    
                // While calling an idle handler, a new message could have been delivered
                // so go back and look again for a pending message without waiting.
                nextPollTimeoutMillis = 0;

    다음은 삽입 작업입니다.
    final boolean enqueueMessage(Message msg, long when) {
            if (msg.isInUse()) {
                throw new AndroidRuntimeException(msg
                        + " This message is already in use.");
            }
            if (msg.target == null && !mQuitAllowed) {
                throw new RuntimeException("Main thread not allowed to quit");
            }
            final boolean needWake;
            synchronized (this) {
                if (mQuiting) {
                    RuntimeException e = new RuntimeException(
                        msg.target + " sending message to a Handler on a dead thread");
                    Log.w("MessageQueue", e.getMessage(), e);
                    return false;
                } else if (msg.target == null) {
                    mQuiting = true;
                }
    
                msg.when = when;
                //Log.d("MessageQueue", "Enqueing: " + msg);
                Message p = mMessages;
                if (p == null || when == 0 || when < p.when) {
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked; // new head, might need to wake up
                } else {
                    Message prev = null;
                    while (p != null && p.when <= when) {
                        prev = p;
                        p = p.next;
                    }
                    msg.next = prev.next;
                    prev.next = msg;
                    needWake = false; // still waiting on head, no need to wake up
                }
            }
            if (needWake) {
                nativeWake(mPtr);
            }
            return true;
        }
    if (msg.isInUse()) {
                throw new AndroidRuntimeException(msg
                        + " This message is already in use.");
            }
            if (msg.target == null && !mQuitAllowed) {
                throw new RuntimeException("Main thread not allowed to quit");
            }
         
    만약 msg가 사용되고 있거나 msg에 대응하는handler가 비어 있거나 종료가 허용되지 않으면 모두 이상을 보고합니다.
    if (mQuiting) {
                    RuntimeException e = new RuntimeException(
                        msg.target + " sending message to a Handler on a dead thread");
                    Log.w("MessageQueue", e.getMessage(), e);
                    return false;
                } else if (msg.target == null) {
                    mQuiting = true;
                }

       
    종료하고 있는지 여부, 종료하면false로 돌아가고, msg에 대응하는handler가 비어 있으면, 종료를true로 설정합니다.
    Message p = mMessages;
                if (p == null || when == 0 || when < p.when) {
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked; // new head, might need to wake up
                } else {
                    Message prev = null;
                    while (p != null && p.when <= when) {
                        prev = p;
                        p = p.next;
                    }
                    msg.next = prev.next;
                    prev.next = msg;
                    needWake = false; // still waiting on head, no need to wake up
                }

    만약 p가 비어 있거나 when=0 또는 삽입 시간이 p.when보다 적을 때, 전송된 메시지를 대기열 헤더에 삽입합니다. (이것은 이전 글에서 언급한 문제, when=0시, 대기열 헤더를 삽입하는 것을 설명합니다.) 그렇지 않으면 대열의 끝을 삽입합니다.
    다음은 삭제입니다.
    final boolean removeMessages(Handler h, int what, Object object,
                boolean doRemove) {
            synchronized (this) {
                Message p = mMessages;
                boolean found = false;
    
                // Remove all messages at front.
                while (p != null && p.target == h && p.what == what
                       && (object == null || p.obj == object)) {
                    if (!doRemove) return true;
                    found = true;
                    Message n = p.next;
                    mMessages = n;
                    p.recycle();
                    p = n;
                }
    
                // Remove all messages after front.
                while (p != null) {
                    Message n = p.next;
                    if (n != null) {
                        if (n.target == h && n.what == what
                            && (object == null || n.obj == object)) {
                            if (!doRemove) return true;
                            found = true;
                            Message nn = n.next;
                            n.recycle();
                            p.next = nn;
                            continue;
                        }
                    }
                    p = n;
                }
                
                return found;
            }
        }
    
        final void removeMessages(Handler h, Runnable r, Object object) {
            if (r == null) {
                return;
            }
    
            synchronized (this) {
                Message p = mMessages;
    
                // Remove all messages at front.
                while (p != null && p.target == h && p.callback == r
                       && (object == null || p.obj == object)) {
                    Message n = p.next;
                    mMessages = n;
                    p.recycle();
                    p = n;
                }
    
                // Remove all messages after front.
                while (p != null) {
                    Message n = p.next;
                    if (n != null) {
                        if (n.target == h && n.callback == r
                            && (object == null || n.obj == object)) {
                            Message nn = n.next;
                            n.recycle();
                            p.next = nn;
                            continue;
                        }
                    }
                    p = n;
                }
            }
        }
    마지막 삭제:
    final void removeCallbacksAndMessages(Handler h, Object object) {
            synchronized (this) {
                Message p = mMessages;
    
                // Remove all messages at front.
                while (p != null && p.target == h
                        && (object == null || p.obj == object)) {
                    Message n = p.next;
                    mMessages = n;
                    p.recycle();
                    p = n;
                }
    
                // Remove all messages after front.
                while (p != null) {
                    Message n = p.next;
                    if (n != null) {
                        if (n.target == h && (object == null || n.obj == object)) {
                            Message nn = n.next;
                            n.recycle();
                            p.next = nn;
                            continue;
                        }
                    }
                    p = n;
                }
            }
        }

          
    더 많은 원본 내용 보기: Android 원본 해석!                           

    좋은 웹페이지 즐겨찾기