經過源碼分析Android 的消息處理機制

#經過源碼分析Android 的消息處理機制java

咱們知道,Android應用是經過消息來驅動的,每個進程被fork以後,都會在該進程的UI線程(主線程)中啓動一個消息隊列,主線程會開啓一個死循環來輪訓這個隊列,處理裏面的消息。數據結構

經過Android進程的入口 ActivityThread#main 方法能夠看到這個邏輯:oop

public static void main(String[] args) {

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        Looper.loop();
	
	//只要運行正常,都不會執行到這段代碼
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

另外,咱們在平時開發中,若是想在子線程中更新UI,必須手動建立一個Looper對象,而且調用它的loop方法:源碼分析

class TestThread extends Thread {
       public Handler mHandler;
	
        public void run() {

	    // 爲當前線程建立Looper對象,同時消息隊列MessageQueue也準備好了
            Looper.prepare();
  
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    
		    //這裏能夠更新UI了

                }
            }; 
  
	    // 開始處理消息隊列中的消息
            Looper.loop();
        }
}

在這兩個過程當中,都看到了Looper的身影,由於Looper就是消息隊列的管理者,咱們調用他的prepareMainLooper和prepare方法,就會爲當前線程建立好消息隊列,調用looper方法就會開始消息隊列的輪詢處理過程。ui

要理解消息處理機制,除了Looper,咱們還要理解其餘類:this

  • MessageQueue:消息隊列
  • Message:消息
  • Handler:消息的處理者

UML類圖

此處輸入圖片的描述

逐個類分析

Looper

先從Looper開始,上面提到的兩個靜態方法 prepareMainLooper()和prepare()的做用就是爲當前線程建立消息隊列,loop()調度這個消息隊列。spa

來看Looper#prepareMainLooper:線程

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
	    
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

prepareMainLooper是經過調用prepare()方法實現,而且經過myLooper()方法獲得了當前線程的Looper,主線程的Looper,就是sMainLooper。code

接着看prepare方法:對象

/**
*  @param uitAllowed 是否容許消息循環退出,在ActivityThread#main 中,咱們建立的是主線程的消息循環,確定不容許退出,其餘地方,則能夠退出
*/
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));
    }

這個方法中爲每個線程new了一個Looper對象,而且設置到sThreadLocal中。sThreadLocal是ThreadLocal類型的變量(線程局部變量),它保證了每個線程有一個本身獨有的Looper對象,

myLooper():

/**
* 從sThreadLocal中獲取當前線程的Looper對象
*/
public static Looper myLooper() {
        return sThreadLocal.get();
}

方法很簡單,就是返回了當前線程所獨有的那個Looper對象。

Looer對象建立好了,同時消息隊列也建立好了,接下來就是讓整個消息機制跑起來,這就須要經過Looper#loop實現:

public static void loop() {

	//獲得當前線程的Looper
        final Looper me = myLooper();

	//沒有Looper對象,直接拋異常
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }

	//獲得當前Looper對應的消息隊列
        final MessageQueue queue = me.mQueue;
	
	//一個死循環,不停的處理消息隊列中的消息,消息的獲取是經過MessageQueue的next()方法實現
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
		
	    //調用Message的target變量(也就是Handler了)的dispatchMessage方法來處理消息
            msg.target.dispatchMessage(msg);

            msg.recycleUnchecked();
        }
    }

首先,獲得當前線程的Looper,再經過Looper獲得當前線程的消息循環,而後輪詢這個消息隊列,處理每個消息,下面我就來看消息隊列MessageQueue以及消息Message。

MessageQueue

MessageQueue就是Message隊列,消息隊列這一數據結構是經過一個Message鏈實現的,Message對象有一個next字段指向它的下一結點。

public final class MessageQueue {
	Message mMessages;

	//消息隊列的初始化,銷燬,輪詢過程,阻塞,喚醒,都是經過本地方法實現的
	private native static long nativeInit();
	private native static void nativeDestroy(long ptr);
	private native static void nativePollOnce(long ptr, int timeoutMillis);
	private native static void nativeWake(long ptr);
	private native static boolean nativeIsIdling(long ptr);


	/**
	*  把消息加入到消息循環中
        *  @param msg 
	*  @param when 是麼時候被執行,在消息隊列中會按照時間排序
 	*/
	boolean enqueueMessage(Message msg, long when) {}

	/**
	*  把消息加入到消息循環中
        *  @param msg 
	*  @param when 何時被執行
 	*/
	boolean enqueueMessage(Message msg, long when) {}
	
        /**移除消息*/
	void removeMessages(Handler h, int what, Object object) {
}

Message對象放入隊列經過enqueueMessage()方法實現,消息的依次獲取是經過next()方法實現,其中當獲取不到可處理Message對象時,該方法會進入等待狀態。

Message

Message就是消息的封裝對象,它實現了Parcelable接口,所以能夠跨進程傳輸。

public final class Message implements Parcelable {
	
	//消息的標識符
	public int what;

	//兩個int形的擴展參數
	public int arg1; 
	public int arg2;

	//一個Object拓展參數
	public Object obj;

	//消息發送者的UID
	public int sendingUid = -1;
	
	//消息帶的data
	Bundle data;

	//target對象,也就是處理當前消息的Handler
	Handler target;

	//指向消息隊列中下一個消息
	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;
}

對消息的管理是經過一個消息池來實現的,由於消息池的存在,咱們在須要建立一個Message對象時,最好使用可複用消息對象的方法來建立它。

正確的使用方法是:

Message msg = mHandler.obtainMessage(WHAT);
msg.sendToTarget();

obtainMessage方法中實際上會調用Message#obtain方法來從消息池中取一個對象。

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
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

錯誤的使用方法:

Message msg = new Message();
msg.what = WHAT;
mHandler.sendMessage(msg);

Handler

Handler 就是消息循環(MessageQueue)中消息(Message)的真正處理者,經過Looper#loop能夠看到:

public static void loop() {

        for (;;) {
            Message msg = queue.next(); // might block

            msg.target.dispatchMessage(msg);
        }
    }

Message的target屬性就是一個Handler對象,對消息的處理其實是經過Handler#dispatchMessage方法來處理的,而dispatchMessag方法會調用Handler#handleMessage方法,咱們能夠重寫handleMessage方法 來真正的處理消息的回調。

相關文章
相關標籤/搜索