Android消息隊列機制

相關文章
Android消息隊列機制
IntentService中的耗時操作

C]
文章將帶你理解 Looper、Handler、Message三者的關係。

一、基本用法

延時應用

定時器應用

//一個定時器
    private Handler handler = new Handler();  
  
    private Runnable myRunnable= new Runnable() {    
        public void run() {  
             
            if (run) {  
                handler.postDelayed(this, 1000);  
                count++;  
            }  
            tvCounter.setText("Count: " + count);  

        }  
    }; 

//然後在其他地方調用

handler.post(myRunnable);

handler.post(myRunnable,time);
    new Thread(new Runnable() {
            @Override
            public void run() {
                //在子線程post一個Runnable對象
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        //這裏是消息處理的方法
                        //這裏運行在主線程。
                    }
                });
            }
        }).start();

一個線程發送消息,另外一個線程接收信息

   //在主線程創建一個Handler對象。
    //重寫Handler的handleMessage方法,這個就是接收並處理消息的方法。
    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //msg就是子線程發送過來的消息。
        }
    };

    //開啓一個子線程
    new Thread(new Runnable() {
            @Override
            public void run() {
                //在子線程發送一個消息。
                Message msg = new Message();
                handler.sendMessage(msg);
            }
        }).start();

 //聲明Handler;
    Handler handler;
    new Thread(new Runnable() {
        @Override
        public void run() {
        //創建當前線程的Looper
            Looper.prepare();
            //在子線程創建handler對象
            handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                //這裏是消息處理,它是運行在子線程的
                }
           };
           //開啓Looper的消息輪詢
           Looper.loop();
       }
   }).start();

   mBanner.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View v) {
       //在主線程發送一個消息到子線程
           Message msg = new Message();
           handler.sendMessage(msg);
       }
   });
//退出輪詢
	Looper.myLooper().quit();
    Looper.myLooper().quitSafely();

獲取主線程

Looper.getMainLooper()是獲取主線程消息隊列。

   new Thread(new Runnable() {
        @Override
        public void run() {
            //獲取主線程的Looper
            Looper looper = Looper.getMainLooper();
            //用主線程的Looper創建Handler
            handler = new Handler(looper) {
                @Override
                public void handleMessage(Message msg) {
                //這裏是運行在主線程的
                }
            };
        }
    }).start();

取消某個消息

handler.removeCallbacksAndMessages(null); //取消所有的回調方法和message

二、原理

Message

\frameworks\base\core\java\android\os\Message.java

public final class Message implements Parcelable {
 /**
     * User-defined message code so that the recipient can identify
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     */
    public int what;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg1;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2;

    /**
     * An arbitrary object to send to the recipient.  When using
     * {@link Messenger} to send the message across processes this can only
     * be non-null if it contains a Parcelable of a framework class (not one
     * implemented by the application).   For other data transfer use
     * {@link #setData}.
     *
     * <p>Note that Parcelable objects here are not supported prior to
     * the {@link android.os.Build.VERSION_CODES#FROYO} release.
     */
    public Object obj;

    /**
     * Optional Messenger where replies to this message can be sent.  The
     * semantics of exactly how this is used are up to the sender and
     * receiver.
     */
    public Messenger replyTo;

    /**
     * Optional field indicating the uid that sent the message.  This is
     * only valid for messages posted by a {@link Messenger}; otherwise,
     * it will be -1.
     */
    public int sendingUid = -1;

    /** If set message is in use.
     * This flag is set when the message is enqueued and remains set while it
     * is delivered and afterwards when it is recycled.  The flag is only cleared
     * when a new message is created or obtained since that is the only time that
     * applications are allowed to modify the contents of the message.
     *
     * It is an error to attempt to enqueue or recycle a message that is already in use.
     */
    /*package*/ static final int FLAG_IN_USE = 1 << 0;

    /** If set message is asynchronous */
    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;

    /** Flags to clear in the copyFrom method */
    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;

    /*package*/ int flags;

    /*package*/ long when;

    /*package*/ Bundle data;

    /*package*/ Handler target;

    /*package*/ Runnable callback;

    // sometimes we store linked lists of these things
    /*package*/ Message next;

    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;

    private static boolean gCheckRecycle = true;
    
    public static Message obtain() {
        synchronized (sPoolSync) {
        	//如果消息池裏面有空閒的消息可以用,則用消息池裏面的。
            if (sPool != null) {
            	//當前這條信息取自消息池裏面的複用
                Message m = sPool;
                //取出消息池的下一條未用信息
                sPool = m.next;
                //當前這條鏈路掐斷
                m.next = null;
                m.flags = 0; // clear in-use flag
                //消息池內信息減1
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    /**
     * Same as {@link #obtain()}, but copies the values of an existing
     * message (including its target) into the new one.
     * @param orig Original message to copy.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Message orig) {
        Message m = obtain();
        m.what = orig.what;
        m.arg1 = orig.arg1;
        m.arg2 = orig.arg2;
        m.obj = orig.obj;
        m.replyTo = orig.replyTo;
        m.sendingUid = orig.sendingUid;
        if (orig.data != null) {
            m.data = new Bundle(orig.data);
        }
        m.target = orig.target;
        m.callback = orig.callback;

        return m;
    }
    
    /**
     * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

    /**
     * Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
     * the Message that is returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @param callback Runnable that will execute when the message is handled.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

	    /**
     * Same as {@link #obtain()}, but sets the values for both <em>target</em> and
     * <em>what</em> members on the Message.
     * @param h  Value to assign to the <em>target</em> member.
     * @param what  Value to assign to the <em>what</em> member.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;

        return m;
    }

	
    /**
     * Return a Message instance to the global pool.
     * <p>
     * You MUST NOT touch the Message after calling this function because it has
     * effectively been freed.  It is an error to recycle a message that is currently
     * enqueued or that is in the process of being delivered to a Handler.
     * </p>
     */
     //回收消息
    public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }

    /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
        	//消息池裏面只保留MAX_POOL_SIZE條空閒的信息,這些信息等待下次使用。
        	//如果實際消息池裏面的數量還不夠50條,則回收這一條信息,
            if (sPoolSize < MAX_POOL_SIZE) {
            	//當前這條信息連接好消息池裏面空閒信息頭
                next = sPool;
                //回收好當前這條信息
                sPool = this;
                //消息池內消息數量加1
                sPoolSize++;
            }
        }
    }
	......//還有set/get參數、序列化等等方法就不羅列了。
}

Message主要的實例屬性是what ,arg1 ,arg2 ,obj,target,callback,next。what是主要是識別身份的,arg1,arg2是int參數,obj是傳遞的對象的,一般是作爲令牌(token)來用,target是所用的handler。callback是Runnable, 需要的時候Run裏面的任務。next是下一條message。message之間連成鏈式連接,這就是消息隊列。消息隊列是單向鏈表。
可以看到public static Message obtain()裏面會根據原來有沒有message來創建,如果有,拿出消息池裏面的message出來用,sPoolSize–,如果沒有則新建一條。message線程池的大小是50條,MAX_POOL_SIZE = 50。消息池實質是回收消息池,消息數目是可以超過50條的,只是消息池會回收最多50條用完的消息來存着備用。
obtain有好幾個重載。

public static Message obtain(Handler h)
public static Message obtain(Handler h, Runnable callback)
public static Message obtain(Handler h, int what)
public static Message obtain(Handler h, int what, Object obj) 
public static Message obtain(Handler h, int what, int arg1, int arg2)
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)

可以根據自己需要創建不同參數的message。
recycle() 是回收message用的。

Handler

post

當用post發送Runnable 時候,它們是進行下面操作。
\frameworks\base\core\java\android\os\Handler.java

    /**
     * Causes the Runnable r to be added to the message queue.
     * The runnable will be run on the thread to which this handler is 
     * attached. 
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean post(Runnable r)
    {
    	//設置定時爲0,用getPostMessage新建了一message
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    //這裏新建了一個Mssage,然後把Runnable 放進去。
	private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
		
	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);
    }
    //處理消息隊列裏面的消息。
	private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //調用消息隊列裏面的enqueueMessage,傳進去消息和延時時間,這個實例裏面傳的是0.
        return queue.enqueueMessage(msg, uptimeMillis);
    }

消息隊列處理
\frameworks\base\core\java\android\os\MessageQueue.java

	//消息排隊,主要是刷選將要執行的消息,並把新消息入隊。
    boolean enqueueMessage(Message msg, long when) {
    	//檢查handler是否有
        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) {
                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 mMessages;這個是鏈頭,也相當於指針的作用了。
            Message p = mMessages;
            boolean needWake;
            //p == null 意味着到尾了。when == 0是立刻執行。when < p.when是比下一個message要早時間執行。
            //此時消息隊列是有序排列的,按時間先後順序。
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                //喚醒執行。設置當前消息爲要執行消息。
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } 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.
               //Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
               //private boolean mBlocked;    
    
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //這個主要是要找msge插入到隊列的位置。
                for (;;) {
                	//用prev來指向當前隊列的某個mssage,如果找到這個message將排在msg之前,p找下一個mssage
                    prev = p;
                    //找下鏈條裏面的一個mssage,直到找完整個隊列或者找到執行時間要比當前時間要長的。
                    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.
            //這裏看是否喚醒線程循環,這裏用到epoll技術
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

上面的操作已經把消息打入隊列裏面了。此時隊列是有序排列的。

sendMessage

發送消息
\frameworks\base\core\java\android\os\Handler.java

    /**
     * Pushes a message onto the end of the message queue after all pending messages
     * before the current time. It will be received in {@link #handleMessage},
     * in the thread attached to this handler.
     *  
     * @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.
     */
    public final boolean sendMessage(Message msg)
    {
    	//設置爲0是立刻執行
        return sendMessageDelayed(msg, 0);
    }

    /**
     * Enqueue a message into the message queue after all pending messages
     * before (current time + delayMillis). You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     *  
     * @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 final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        //SystemClock.uptimeMillis()表示系統開機到當前的時間總數,單位是毫秒,
        //但是,當系統進入深度睡眠(CPU休眠、屏幕休眠、設備等待外部輸入)時間就會停止,
        //但是不會受到時鐘縮放、空閒或者其他節能機制的影響。
        //這裏是延時加當前系統時間,表示未來要執行的時間點。
        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);
    }
    //把消息放到隊列裏面
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

queue.enqueueMessage是根據放進去的消息來排列,上面已經說了原理。

postDelayed

基本原理和sendMessage一樣

\frameworks\base\core\java\android\os\Handler.java

    public final boolean postDelayed(Runnable r, long delayMillis)
    {
	    //生成一個新的mssage,設置好延時毫秒
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
    
    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);
    }
	private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

延時在消息從系統啓動時間算起,看延時到哪個點,然後放到消息隊列裏面。

removeCallbacks

移除Callback
\frameworks\base\core\java\android\os\Handler.java

    /**
     * Remove any pending posts of Runnable r that are in the message queue.
     */
    public final void removeCallbacks(Runnable r)
    {
        mQueue.removeMessages(this, r, null);
    }

從消息隊列裏匹對消息然後移除該消息
\frameworks\base\core\java\android\os\MessageQueue.java

    void removeMessages(Handler h, Runnable r, Object object) {
        if (h == null || r == null) {
            return;
        }

        synchronized (this) {
        	//mMessages是消息隊列的隊首
            Message p = mMessages;

            // Remove all messages at front.
            //這裏用循環來匹配消息內容,如果找到了,則把該條massage移除。
            //從隊首開始刪除,如果刪除了隊首就指向接下來的元素。
            while (p != null && p.target == h && p.callback == r
                   && (object == null || p.obj == object)) {
                   //令mMessages爲下一個mssage 
                Message n = p.next;
                mMessages = n;
                //這裏回收消息
                p.recycleUnchecked();
                p = n;
            }

            // Remove all messages after front.
            //如果不爲結尾繼續循環,移除找到的Message
            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.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

handler.removeCallbacksAndMessages(null);是用來清空消息隊列的,此時匹對的消息的obj都是爲null。

Looper

經過handler把消息壓進了消息隊列,需要一個循環逐一拿出來用,這個循環是Looper。在調用中用Looper.loop();來開啓。

 //聲明Handler;
    Handler handler;
    new Thread(new Runnable() {
        @Override
        public void run() {
        //創建當前線程的Looper
            Looper.prepare();
            //在子線程創建handler對象
            handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                //這裏是消息處理,它是運行在子線程的
                }
           };
           //開啓Looper的消息輪詢
           Looper.loop();
       }
   }).start();

\frameworks\base\core\java\android\os\Looper.java

public final class Looper {
    /*
     * API Implementation Note:
     *
     * This class contains the code required to set up and manage an event loop
     * based on MessageQueue.  APIs that affect the state of the queue should be
     * defined on MessageQueue or Handler rather than on Looper itself.  For example,
     * idle handlers and sync barriers are defined on the queue whereas preparing the
     * thread, looping, and quitting are defined on the looper.
     */

    private static final String TAG = "Looper";

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    //主線程調用的Looper
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

    private Printer mLogging;
    private long mTraceTag;

    /* If set, the looper will show a warning log if a message dispatch takes longer than time. */
    private long mSlowDispatchThresholdMs;

     /** 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()}.
      */
      //線程裏面構建一個新的looper,Looper.prepare();每一個Looper對象會和一個線程關聯 
      //Looper對象創建時會創建一個MessageQueue,一般其他線程要這個looper要調用Looper.prepare()
    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));
    }

    /**
     * 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()}
     */
	//這個主線程的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();
        }
    }

    /**
     * Returns the application's main looper, which lives in the main thread of the application.
     */
     //這個是獲取主線程的Looper,UI線程的大循環是這裏取得
     //如果你想建立一個UI更新的handler,可以這麼操作
     //Handler mhandler = new Handler(Looper.getMainLooper);
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
     //Looper.loop就是這個循環
    public static void loop() {
    	//sThreadLocal裏面獲取當前的ThreadLocal,其實是Looper對象。
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //獲取當前Looper的消息隊列
        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 (;;) {
        	//查找下一條消息,消息隊列裏面保存有mMessages,這個是鏈頭
            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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            //SystemClock.uptimeMillis() 從開機到現在的毫秒數(手機睡眠的時間不包括在內)
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
            	//這裏調用的是handler裏面的dispatchMessage(Message msg)方法,
            	//它將會調用我們自己定義的handleMessage方法。
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //根據需要算時間差
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            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();
        }
    }


    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
     //獲取當前thread的threadload
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

    /**
     * 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.
     */
     //獲取threadload裏面的消息隊列
    public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

    /**
     * Returns true if the current thread is this looper's thread.
     */
    public boolean isCurrentThread() {
        return Thread.currentThread() == mThread;
    }

    ......
}

大致的處理過程是獲取當前線程的looper,然後獲取looper裏面的消息隊列,然後一個個處理消息隊列裏面的消息,處理完就回收消息。

loop()裏面的msg.target.dispatchMessage(msg);是調用了我們定義消息處理動作了。dispatchMessage裏面是調用handleCallback或者handleMessage。dispatchMessage是Handler裏面的方法。
\frameworks\base\core\java\android\os\Handler.java

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
    	//判斷是否爲空
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
        	//這裏檢查Callback 有沒有被實現,如果有則調用改接口的handleMessage
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //這一步是調用我們定義的內容
            handleMessage(msg);
        }
    }
    
	//如果有callback則run,callback是一個Runnable
    private static void handleCallback(Message message) {
        message.callback.run();
    }

//handleMessage是我們定義的處理信息的動作了。
    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     *
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

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

看下Message msg = queue.next();裏面執行了什麼。
\frameworks\base\core\java\android\os\MessageQueue.java

public final class MessageQueue {
	Message mMessages;
	......
    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
        	//這個nextPollTimeoutMillis 是上一次消息處理適合設置的下一次喚醒時間,沒到時間就休眠
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
			//阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                //這裏主要是檢索嚇一條mssage。如果找到就返回。
                //獲取當前的時間。這個時間是系統開機時間,用毫秒錶示。
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                //獲取消息隊列的頭
                Message msg = mMessages;
                //如果handler沒寫好就一直找到下一個設置好target的message爲止。
                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) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        //下一條信息還沒到時間執行。
                        //設置下一次執行的喚醒時間
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        //下一條信息到時間執行了,取出來。
                        mBlocked = false;
                        //如果該條信息還有前一條信息則設置好前一條信息的鏈路
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                        	//設置隊列頭爲下一條信息
                            mMessages = msg.next;
                        }
                        //斷開鏈路,單獨取出來。
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    //沒有消息了
                    nextPollTimeoutMillis = -1;
                }
                //消息都已經執行,並清空了。執行下面的流程。
                //其實是設置epoll的喚醒時間

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
                //IdleHandler 可以用來提升性能,主要用在我們希望能夠在當前線程消息隊列空閒時做些事情
                //(譬如 UI 線程在顯示完成後,如果線程空閒我們就可以提前準備其他內容)的情況下,不過最好不要做耗時操作。

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    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.
            //循環遍歷所有IdleHandler
            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.
            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;
        }
    }
    ......
 }

MessageQueue.mMessages->( Message並且Message.next)-> ( Message並且Message.next)->…–>null 構成一個單向鏈表。Message msg = queue.next();是拿出下一條message出來,如果設置了定時,沒到時間就會拿出來。拿出來後通過msg.target.dispatchMessage(msg);來執行。這個looper是個循環,有消息就處理,沒消息就阻塞,通過epoll機制來調整運行狀態。在塞信息進入消息隊列適合會判斷是否喚醒循環nativeWake(mPtr);

next裏面的阻塞代碼是下面這個。阻塞是發生在nativePollOnce方法,在native層使用了epoll機制來等待消息。

//private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
for (;;) {
    if (nextPollTimeoutMillis != 0) {
        Binder.flushPendingCommands();
    }
    nativePollOnce(ptr, nextPollTimeoutMillis);

    //...
}

退出消息隊列

\frameworks\base\core\java\android\os\Looper.java

    /**
     * Quits the looper.
     * <p>
     * Causes the {@link #loop} method to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @see #quitSafely
     */
    public void quit() {
        mQueue.quit(false);
    }

    /**
     * Quits the looper safely.
     * <p>
     * Causes the {@link #loop} method to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * However pending delayed messages with due times in the future will not be
     * delivered before the loop terminates.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p>
     */
    public void quitSafely() {
        mQueue.quit(true);
    }

\frameworks\base\core\java\android\os\MessageQueue.java

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }
//可以從代碼看出退出隊列會把隊列清空
    private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }

    private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                //如果隊列頭不是未來要執行的message的話,則繼續找,找到爲止。然後再回收那些未來某個點執行的message。
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    //在鏈表裏面找到不是立即執行的message,然後斷開,再回收那些未來要執行的message
                    p = n;
                }
                //這裏是爲了鏈表斷開那些定時和沒定時的message
                p.next = null;
                //定時執行的message將被回收。
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }

looper裏面的quit()或quitSafely()可以退出消息隊列,從代碼看出退出隊列會把隊列清空。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章