架構(gòu)設(shè)計(jì)分析(二)Android 消息機(jī)制篇一 Handler流程分析

來點(diǎn)前奏說明

當(dāng)你打開這個(gè)文檔的時(shí)候,你已經(jīng)做好準(zhǔn)備了秤标,話不多說開搞挫鸽。
本文以Android 9.0 版本進(jìn)行分析,當(dāng)然你也可以在線看源碼
在線源碼查看
Android源碼下載編譯
在此特別說明英染,我這篇主要分析流程和注釋。我把英語注釋也粘貼了被饿,大家自己去翻譯自己消化税迷,個(gè)人意見重點(diǎn)是流程+注釋,流程+注釋锹漱,流程+注釋。

產(chǎn)生Handler原因:
  • 主線程不能做耗時(shí)操作
  • 子線程不能更新UI
  • 多個(gè)線程并發(fā)更新UI的同時(shí)保證線程安全
為什么不能在子線程更新UI

詳細(xì)請參考 架構(gòu)設(shè)計(jì)分析(二)Android消息機(jī)制之為什么不能在子線程中更新UI
framework/base/core/java/android/view/ViewRootImpl.java

    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }
  • mThread是UI線程慕嚷,這里會檢查當(dāng)前線程是不是UI線程哥牍。那么為什么onCreate()方法內(nèi)沒有進(jìn)行這個(gè)檢查呢?因?yàn)锳ctivity的生命周期內(nèi)喝检,在onCreate()方法中嗅辣,UI處于創(chuàng)建過程,對用戶來說界面還不可見挠说,直到onStart()方法之后界面可視澡谭,在到onResume()方法后界面開始交互。在某種程度來講损俭,在onCreate()方法中不能算是更新UI,只能說是適配了UI或者是設(shè)置UI的屬性蛙奖。這個(gè)時(shí)候不會調(diào)用ViewRootImpl.checkThread()方法潘酗,因?yàn)閂iewRootImpl還沒創(chuàng)建。而在onResume()方法之后雁仲,ViewRootImpl才被創(chuàng)建仔夺,這個(gè)時(shí)候去交互UI才算是更新UI。
   private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;

        if (DBG) {
            System.out.println("======================================");
            System.out.println("performTraversals");
            host.debug();
        }

        if (host == null || !mAdded)
            return;

        mIsInTraversal = true;
        mWillDrawSoon = true;
        boolean windowSizeMayChange = false;
        boolean newSurface = false;
        boolean surfaceChanged = false;
        WindowManager.LayoutParams lp = mWindowAttributes;

        int desiredWindowWidth;
        int desiredWindowHeight;

        final int viewVisibility = getHostVisibility();
        final boolean viewVisibilityChanged = !mFirst
                && (mViewVisibility != viewVisibility || mNewSurfaceNeeded
                // Also check for possible double visibility update, which will make current
                // viewVisibility value equal to mViewVisibility and we may miss it.
                || mAppVisibilityChanged);
        mAppVisibilityChanged = false;
        final boolean viewUserVisibilityChanged = !mFirst &&
                ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));

        WindowManager.LayoutParams params = null;
        if (mWindowAttributesChanged) {
            mWindowAttributesChanged = false;
            surfaceChanged = true;
            params = lp;
        }
  • setContentView只是建立了View樹并沒有進(jìn)行渲染工作攒砖,真正的渲染工作是在onResume()方法之后也就是建立了VIew樹缸兔。因此我們可以通過findViewById()來獲取View對象,但是由于沒有進(jìn)行渲染視圖的工作吹艇,也就是沒有執(zhí)行ViewRootImpl.performTraversals()惰蜜。同樣View也不會執(zhí)行onMeasure()。如果在onResume()方法中直接獲取View.getHeight()和View.getWidth()得到的結(jié)果都是0受神。
  • zhangbin: onResume getHeight:0 getWidth:0
Android消息機(jī)制

Android 的消息機(jī)制主要是指Handler得運(yùn)行機(jī)制


消息機(jī)制流程圖.png

以上模型的解釋:
1.以Handler的sendMessage方法為例抛猖,當(dāng)發(fā)送一個(gè)消息后,會將此消息加入消息隊(duì)列MessageQueue中路克。
2.Looper負(fù)責(zé)去遍歷消息隊(duì)列并且將隊(duì)列中的消息分發(fā)給對應(yīng)的Handler進(jìn)行處理樟结。
3.在Handler的handleMessage方法中處理該消息,這就完成了一個(gè)消息的發(fā)送和處理過程精算。
這里從圖中可以看到參與消息處理有四個(gè)對象瓢宦,它們分別是 Handler, Message, MessageQueue,Looper灰羽。

為什么Handler構(gòu)造方法里面的Looper不是直接new呢驮履?
  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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

代碼也很好解釋,如果Handler構(gòu)造方法中new Looper無法保證Looper唯一廉嚼。目前使用的

 /**
     * 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();
    }
為什么MessageQueue要放在Looper私有構(gòu)造方法中初始化玫镐?
   private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

因?yàn)橐粋€(gè)線程對應(yīng)一個(gè)Looper,所有在Looper構(gòu)造方法初始化可以保證mQueue也是唯一的,也就是一個(gè)Thread對應(yīng)一個(gè)Looper對應(yīng)一個(gè)MessageQueue對象怠噪。

分析入口ActivityThread的main方法

ActivityThread就是我們常說的主線程或UI線程恐似,ActivityThread的main方法是整個(gè)APP的入口,這個(gè)(main里面的attach這里) 也是Activity啟動分析的入口傍念。有童鞋問這個(gè)文件目錄在哪矫夷,frameworks/base/core/java/android/app/ActivityThread.java

    public static void main(String[] args) {
        ... ...
        Looper.prepareMainLooper();                      

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);


        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

     
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
1-1、主線程Looper的prepareMainLooper()方法
  • 主要看注釋憋槐,注釋看個(gè)幾遍不為過双藕,先看下Looper的注釋。
/**
  * Class used to run a message loop for a thread.  Threads by default do
  * not have a message loop associated with them; to create one, call
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  *
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  *
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>
  */
  • prepareMainLooper()方法
  /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
1-1-1阳仔、 prepare()方法忧陪,還是主要看注釋,注釋看個(gè)幾遍不為過。

false表示主線程不準(zhǔn)許退出嘶摊,主動掉prepare()值為true延蟹。這里的quitAllowed參數(shù),最終會傳遞給MessageQueue更卒,當(dāng)調(diào)用MessageQueue的quit方法時(shí)等孵,會判斷這個(gè)參數(shù),如果是主線程蹂空,也就是quitAllowed參數(shù)為false時(shí)俯萌,會拋出異常。

    /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void 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));
    }

sThreadLocal的變量定義上枕,繼續(xù)看注釋咐熙。

// sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
1-1-2、ThreadLocal的set和get方法辨萍,主要實(shí)現(xiàn)線程的單例棋恼,上面是Looper的單例。
  • 先看下人家寫這個(gè)類的注釋锈玉,我特意百度了一下這個(gè)人爪飘。他是Google 的首席 Java 架構(gòu)師人物介紹
/**
 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 *
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread's id is assigned the first time it invokes {@code ThreadId.get()}
 * and remains unchanged on subsequent calls.
 * <pre>
 * import java.util.concurrent.atomic.AtomicInteger;
 *
 * public class ThreadId {
 *     // Atomic integer containing the next thread ID to be assigned
 *     private static final AtomicInteger nextId = new AtomicInteger(0);
 *
 *     // Thread local variable containing each thread's ID
 *     private static final ThreadLocal&lt;Integer&gt; threadId =
 *         new ThreadLocal&lt;Integer&gt;() {
 *             &#64;Override protected Integer initialValue() {
 *                 return nextId.getAndIncrement();
 *         }
 *     };
 *
 *     // Returns the current thread's unique ID, assigning it if necessary
 *     public static int get() {
 *         return threadId.get();
 *     }
 * }
 * </pre>
 * <p>Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the {@code ThreadLocal}
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist).
 *
 * @author  Josh Bloch and Doug Lea
 * @since   1.2
 */
1-1-3、get方法
    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
1-1-4拉背、setInitialValue()方法
    /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
1-1-5师崎、initialValue()方法
    /**
     * Returns the current thread's "initial value" for this
     * thread-local variable.  This method will be invoked the first
     * time a thread accesses the variable with the {@link #get}
     * method, unless the thread previously invoked the {@link #set}
     * method, in which case the {@code initialValue} method will not
     * be invoked for the thread.  Normally, this method is invoked at
     * most once per thread, but it may be invoked again in case of
     * subsequent invocations of {@link #remove} followed by {@link #get}.
     *
     * <p>This implementation simply returns {@code null}; if the
     * programmer desires thread-local variables to have an initial
     * value other than {@code null}, {@code ThreadLocal} must be
     * subclassed, and this method overridden.  Typically, an
     * anonymous inner class will be used.
     *
     * @return the initial value for this thread-local
     */
    protected T initialValue() {
        return null;
    }
1-1-6、set方法
    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
1-1-7椅棺、Looper的構(gòu)造方法犁罩。
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
1-1-8、MessageQueue的構(gòu)造方法两疚。
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
1-2床估、myLooper()分析
    /**
     * 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();
    }
2-1、ActivityThread中的main方法的getHandler()方法
if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
    final Handler getHandler() {
        return mH;
    }
    final H mH = new H();
    class H extends Handler {
}
3-1诱渤、主線程Looper的loop()方法

這個(gè)循環(huán)取出Message并交給Handler處理msg.target.dispatchMessage(msg);

    /**
     * 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();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

        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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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();
        }
    }
3-1-1丐巫、取出Looper
final Looper me = myLooper();
3-1-2、MessageQueue
final MessageQueue queue = me.mQueue;
3-1-3勺美、Message
Message msg = queue.next(); // might block
3-1-4鞋吉、Handler
/**
 * A Handler allows you to send and process {@link Message} and Runnable
 * objects associated with a thread's {@link MessageQueue}.  Each Handler
 * instance is associated with a single thread and that thread's message
 * queue.  When you create a new Handler, it is bound to the thread /
 * message queue of the thread that is creating it -- from that point on,
 * it will deliver messages and runnables to that message queue and execute
 * them as they come out of the message queue.
 * 
 * <p>There are two main uses for a Handler: (1) to schedule messages and
 * runnables to be executed as some point in the future; and (2) to enqueue
 * an action to be performed on a different thread than your own.
 * 
 * <p>Scheduling messages is accomplished with the
 * {@link #post}, {@link #postAtTime(Runnable, long)},
 * {@link #postDelayed}, {@link #sendEmptyMessage},
 * {@link #sendMessage}, {@link #sendMessageAtTime}, and
 * {@link #sendMessageDelayed} methods.  The <em>post</em> versions allow
 * you to enqueue Runnable objects to be called by the message queue when
 * they are received; the <em>sendMessage</em> versions allow you to enqueue
 * a {@link Message} object containing a bundle of data that will be
 * processed by the Handler's {@link #handleMessage} method (requiring that
 * you implement a subclass of Handler).
 * 
 * <p>When posting or sending to a Handler, you can either
 * allow the item to be processed as soon as the message queue is ready
 * to do so, or specify a delay before it gets processed or absolute time for
 * it to be processed.  The latter two allow you to implement timeouts,
 * ticks, and other timing-based behavior.
 * 
 * <p>When a
 * process is created for your application, its main thread is dedicated to
 * running a message queue that takes care of managing the top-level
 * application objects (activities, broadcast receivers, etc) and any windows
 * they create.  You can create your own threads, and communicate back with
 * the main application thread through a Handler.  This is done by calling
 * the same <em>post</em> or <em>sendMessage</em> methods as before, but from
 * your new thread.  The given Runnable or Message will then be scheduled
 * in the Handler's message queue and processed when appropriate.
 */
   /**
     * 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);
        }
    }
final Callback mCallback;
    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }

這個(gè)就是使用Handler重寫的這個(gè)方法

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

4-1、Handler結(jié)合源碼分析使用

4-1-1励烦、Handler構(gòu)造方法
Handler handler = new Handler();
重寫handleMessage方法
    /**
     * Default constructor associates this handler with the {@link Looper} for the
     * current thread.
     *
     * If this thread does not have a looper, this handler won't be able to receive messages
     * so an exception is thrown.
     */
    public Handler() {
        this(null, false);
    }
    /**
     * Use the {@link Looper} for the current thread with the specified callback interface
     * and set whether the handler should be asynchronous.
     *
     * Handlers are synchronous by default unless this constructor is used to make
     * one that is strictly asynchronous.
     *
     * Asynchronous messages represent interrupts or events that do not require global ordering
     * with respect to synchronous messages.  Asynchronous messages are not subject to
     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
     *
     * @param callback The callback interface in which to handle messages, or null.
     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
     *
     * @hide
     */
    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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

看到此我相信有些童鞋已經(jīng)看到Looper.myLooper()這個(gè)方法了

4-1-1、myLooper()分析

參考1-2泼诱、myLooper()分析

4-1-2坛掠、發(fā)送消息,任意一個(gè)方法都可以

mHandler.sendEmptyMessageDelayed(DRAG_SHOW,1500);

4-1-3、最終都會走到sendMessageAtTime()
    /**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    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);
    }
4-1-4屉栓、sendMessageAtTime()調(diào)用 enqueueMessage() 這個(gè)隊(duì)列是單向鏈表維護(hù) 先進(jìn)先出
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
4-1-5舷蒲、MessageQueue的enqueueMessage()這里面根據(jù)時(shí)間將消息插入合適的鏈表中
    boolean enqueueMessage(Message msg, long when) {
        //每一個(gè)普通的Message必須有一個(gè)target
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {  //正在退出時(shí),回收msg 加入到消息池中
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //p為null代表MessageQueue沒有消息
            if (p == null || when == 0 || when < p.when) {
                //when比最新插入對象的時(shí)間還要小就替換
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;//進(jìn)行喚醒
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
               // 將消息按照時(shí)間插入到MessageQueue 一般不需要喚醒事件隊(duì)列
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
4-1-6友多、有消息之后主線程Looper的loop()循環(huán)取出消息交給在重寫的handleMessage進(jìn)行處理

分析參考3-1牲平、主線程Looper的loop()方法
MessageQueue # next

Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {//若消息循環(huán)已退出
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration 首次迭代
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            // 阻塞操作,當(dāng)?shù)却齨extPollTimeoutMillis時(shí)長域滥,或者消息隊(duì)列被喚醒纵柿,都會返回
            // 陷入阻塞,等待被喚醒
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // 當(dāng)消息的Handler為空時(shí)启绰,則查詢異步消息
                // msg.target == null表示此消息為消息屏障(通過postSyncBarrier方法發(fā)送來的)
                // 如果發(fā)現(xiàn)了一個(gè)消息屏障昂儒,會循環(huán)找出第一個(gè)異步消息(如果有異步消息的話),所有同步消息都將忽略(平常發(fā)送的一般都是同步消息)
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        //當(dāng)異步消息觸發(fā)時(shí)間大于當(dāng)前時(shí)間委可,則設(shè)置下一次輪詢的超時(shí)時(shí)長
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //正常取出消息
                        //設(shè)置mBlocked = false代表目前沒有阻塞
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        //設(shè)置消息的使用狀態(tài)渊跋,即flags |= FLAG_IN_USE
                        msg.markInUse();
                        return msg;//成功地獲取MessageQueue中的下一條即將要執(zhí)行的消息
                    }
                } else {
                    // 沒有消息,會一直阻塞着倾,直到被喚醒
                    nextPollTimeoutMillis = -1;
                }

                // 消息正在退出
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // 當(dāng)消息隊(duì)列為空拾酝,或者是消息隊(duì)列的第一個(gè)消息時(shí)
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    //沒有idle handlers 需要運(yùn)行,則循環(huán)并等待卡者。
                    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.
            //只有第一次循環(huán)時(shí)蒿囤,會運(yùn)行idle handlers,執(zhí)行完成后虎眨,重置pendingIdleHandlerCount為0.
            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(TAG, "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.
            //重置idle handler個(gè)數(shù)為0蟋软,以保證不會再次重復(fù)運(yùn)行
            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.
            //當(dāng)調(diào)用一個(gè)空閑handler時(shí),一個(gè)新message能夠被分發(fā),因此無需等待可以直接查詢pending message.
            //重置數(shù)量,保證每次 next() 時(shí)其屏,只會執(zhí)行一次 IdleHandler 方法疙驾。
            nextPollTimeoutMillis = 0;
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市慕爬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖譬重,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異罐氨,居然都是意外死亡臀规,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門栅隐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來塔嬉,“玉大人玩徊,你說我怎么就攤上這事〗骶浚” “怎么了恩袱?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胶哲。 經(jīng)常有香客問我畔塔,道長,這世上最難降的妖魔是什么鸯屿? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任澈吨,我火速辦了婚禮,結(jié)果婚禮上碾盟,老公的妹妹穿的比我還像新娘棚辽。我一直安慰自己,他們只是感情好冰肴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布屈藐。 她就那樣靜靜地躺著,像睡著了一般熙尉。 火紅的嫁衣襯著肌膚如雪联逻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天检痰,我揣著相機(jī)與錄音包归,去河邊找鬼。 笑死铅歼,一個(gè)胖子當(dāng)著我的面吹牛公壤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播椎椰,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼厦幅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了慨飘?” 一聲冷哼從身側(cè)響起确憨,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瓤的,沒想到半個(gè)月后休弃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡圈膏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年塔猾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稽坤。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡桥帆,死狀恐怖医增,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情老虫,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布茫多,位于F島的核電站祈匙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏天揖。R本人自食惡果不足惜夺欲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望今膊。 院中可真熱鬧些阅,春花似錦、人聲如沸斑唬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恕刘。三九已至缤谎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間褐着,已是汗流浹背坷澡。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留含蓉,地道東北人频敛。 一個(gè)月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像馅扣,于是被迫代替她去往敵國和親斟赚。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容