1. 程式人生 > >Handler Looper Message的原始碼分析

Handler Looper Message的原始碼分析

Handler

Handler是用來分發和處理訊息的,通常我們建立Handler都是使用其無引數的構造方法


public Handler() {
    this(null, false);
}

其內部呼叫的是2個引數的構造方法


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) { //大概的意思就是建立Handler的匿名內部類,成員類,區域性類的時候會給出下面log的提示 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName()); } } //獲取當前執行緒的Looper物件,如果是在UI執行緒(四大元件)中建立Handler,那麼這裡拿到的就是主執行緒的Looper mLooper = Looper.myLooper(); if (mLooper == null) { //在子執行緒中建立Handler如果沒有事先初始化Looper,是拿不到Looper的,因此會拋異常 throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"
); } //記錄Looper物件內的MessageQueue訊息佇列 mQueue = mLooper.mQueue; //記錄傳遞進來的callback介面,注意這裡的Callback介面是定義在Handler類內部的. mCallback = callback; //記錄是否是非同步,預設構造方法傳入的是false,表示同步處理訊息 mAsynchronous = async; } //內部介面 public interface Callback { public boolean handleMessage(Message msg); }

分析上面的原始碼可以發現,在new Handler的時候,內部會得到一個Looper物件,以及該Looper的MessageQueue物件.
同時提到了 "Can't create handler inside thread that has not called Looper.prepare()");這個異常,僅當Looper獲取為空的時候丟擲.

另外,引數async表示此建立的Handler是否用於處理非同步訊息,如果是則需要通過Message的方式來非同步處理,而如果不是,那就通過postRunnable的方式來處理同步的訊息.


Looper

上面建立handler的時候提到了Looper,那麼現在我們來分析下Looper的原始碼

public final class Looper {
    private static final String TAG = "Looper";

    // 通過ThreadLocal繫結Looper物件到當前執行緒,預設情況下sThreadLocal.get()會返回null,除非你事先呼叫了prepare()方法.
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    // 主執行緒的Looper物件,在主執行緒中建立Handler,預設會初始化一個Looper與主執行緒繫結
    private static Looper sMainLooper; 
	//訊息佇列,每個Looper物件內部都有一個訊息佇列
    final MessageQueue mQueue;
    //當前執行緒
    final Thread mThread;
	//log輸出物件
    private Printer mLogging;

     /** 
     * 初始化Looper,給當前執行緒繫結一個Looper物件,該方法建立的Looper,預設是支援結束loop的
      */
    public static void prepare() {
        prepare(true);
    }
	/**
	* 私有的初始化Looper物件方法
	* 引數quitAllowed表示是否支援結束loop,通過呼叫quit()或者quitSafely()方法進行結束
	*/
    private static void prepare(boolean quitAllowed) {
    	//一個執行緒只能繫結一次Looper
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //建立一個Looper,並將Looper繫結到當前執行緒中
        sThreadLocal.set(new Looper(quitAllowed));
    }
	
	/**
	* 私有構造方法,因此建立Looper必須要通過prepare方法建立
	*/
	private Looper(boolean quitAllowed) {
		//初始化該Looper的MessageQueue物件,Handler傳送訊息的時候會用到mQueue,它用於儲存Handler傳送的Message
        mQueue = new MessageQueue(quitAllowed);
        //獲取當前執行緒,作為sThreadLocal的key,value就是該繫結的Looper物件
        mThread = Thread.currentThread();
    }

	
    /**
     *初始化主執行緒的Looper物件,由android系統呼叫,不需要我們自己呼叫
     */
    public static void prepareMainLooper() {
    	//初始化Looper,主執行緒建立的Looper是不需要結束loop的,如果手動呼叫結束方法會丟擲異常
        prepare(false);
       
        //主執行緒的Looper只能由android系統初始化,我們自己初始化會拋異常
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
             //記錄主執行緒的Looper
            sMainLooper = myLooper();
        }
    }

    /** 獲取主執行緒的Looper
     */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

 /**
     * 返回與當前執行緒相關Looper物件,如果返回null,則說明當前執行緒沒有關聯Looper
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

  
    /**
     * 返回當前執行緒繫結的Looper物件內的MessageQueue ,這個必須在Looper中執行的執行緒中呼叫,否則會報空指標異常
     */
    public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }
	

    /**
     * 輪詢MessageQueue中的Message,如果是子線則需要手動呼叫loop方法輪詢處理訊息
     */
    public static void loop() {
    	//1.獲取當前執行緒繫結的looper物件
        final Looper me = myLooper();
        if (me == null) {
        //在子執行緒中建立Handler,如果沒有呼叫Looper.prepare(),那麼myLooper()方法是獲取不到Looper物件的.則會拋異常,
        //而主執行緒由於系統已經呼叫了prepareMainLooper方法,因此沒有這個問題
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //2.獲取Looper的訊息佇列
        final MessageQueue queue = me.mQueue;

        // ...

        for (;;) {
       		 //3.這裡是死迴圈,從MessageQueue中遍歷所有的Message物件
            Message msg = queue.next(); // might block (next方法是阻塞的方法)
			
			//訊息為null,結束loop
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

           // ...
           
           /* 4.獲取Message所關聯的target,這裡的target其實就是Handler物件,後面會解釋
           呼叫關聯的Handler物件的dispatchMessage方法,將訊息傳遞給Handler去處理*/
            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // ...

			// 釋放訊息,清空Message中儲存的各種引用
            msg.recycle();
        }
    }

   
    
    /**
     * 判斷當前Looper內的執行緒是否就是當前執行緒
     */
    public boolean isCurrentThread() {
        return Thread.currentThread() == mThread;
    }

    /**
     * 非安全的退出Looper,會導致loop方法終止,退出時並不保證訊息佇列中的所有message都已處理完畢
     */
    public void quit() {
        mQueue.quit(false);
    }

    /**
	* 安全的結束Looper,loop方法僅當訊息佇列中的所有message都處理完畢後才終止,但是不包括延時處理的message
     */
    public void quitSafely() {
        mQueue.quit(true);
    }

    

    /**
     * 獲取與之Looper關聯的執行緒物件
     */
    public Thread getThread() {
        return mThread;
    }

  ...

通過分析Looper的原始碼,我們可以瞭解到Looper類是用來輪詢當前執行緒相關聯的Message物件的,每輪詢一個message,就會通過該message持有的Handler的引用也就是target去處理訊息,具體是通過dispatchMessage方法分發處理訊息,稍後會講到.

預設建立的執行緒是沒有與任何Looper進行關聯的,必須要先通過Looper的prepare()靜態方法來建立並通過ThreadLocal進行關聯繫結,接著輪詢處理訊息的時候必須要手動呼叫loop方法進行處理.


子執行緒建立Handler

下面是一個經典的子執行緒中建立Handelr,並通過Looper輪詢和處理訊息的例子:

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
    	//初始化Looper
        Looper.prepare();
        //建立處理訊息的Handler,最開始已經分析過子執行緒中建立Handler,必須要先有Looper
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };
        //開始輪詢
        Looper.loop();
    }
}

在主執行緒中建立Handler的,系統已經幫我們完成了Looper.prepare();和 Looper.loop();的工作了,因此我們可以直接使用Handler.


疑問

分析Looper原始碼的時候有以下2個疑問待解決,定位到loop方法中的msg.target.dispatchMessage(msg);

1.target是何時與Message進行關聯的呢?
2.dispatchMessage是如何處理輪詢出來的訊息(message物件)?

target的由來

target是Message物件的一個屬性,要知道它是啥時候賦值的,那麼我們可以先Message的建立說起,Message有多中方式建立,通過空參的構造方法建立,或者通過其多個靜態的obtain方法建立.
空參的構造方法內一行程式碼也沒有,看不到有啥有用的線索,obtain方法倒是有幾個可以直接看出對target的賦值宣告,例如:

...
public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;

    return m;
}


public static Message obtain(Handler h, Runnable callback) {
    Message m = obtain();
    m.target = h;
    m.callback = callback;

    return m;
}

...

但是這些靜態方法並不是建立Message的唯一途徑,因此都不能保證target就已經關聯了Handler物件,那麼我們只能從Handler的sendMessage方法入手了.

public final boolean sendMessage(Message msg) {
	//繼續往下呼叫sendMessageDelayed方法,該方法是一個延時傳送訊息的方法,這裡傳遞0表示馬上傳送.
	return sendMessageDelayed(msg, 0);
}

接著sendMessageDelayed方法

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
	if (delayMillis < 0) {
	 	delayMillis = 0;
	}
	//繼續往下呼叫sendMessageAtTime,功能是一樣的,名字不同而已.
	return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

}

接著sendMessageAtTime方法

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
	//判斷此時的Looper的MessageQueue是否為null,是者列印警告log
	MessageQueue queue = mQueue;
	if (queue == null) {
	    RuntimeException e = new RuntimeException(
	            this + " sendMessageAtTime() called with no mQueue");
	    Log.w("Looper", e.getMessage(), e);
	    return false;
	}
	//繼續往下呼叫enqueueMessage
	return enqueueMessage(queue, msg, uptimeMillis);
}

接著enqueueMessage方法

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
	/*ok,終於找到看到真相了.this就是當前建立的Handler,也就是說我們通過Handler的sendMessage傳送Message的時候,
	該Message物件將該Handler儲存到它的target變數中.*/
	msg.target = this;
	if (mAsynchronous) {
	    msg.setAsynchronous(true);
	}
	//這一步是將訊息存放到訊息佇列中
	return queue.enqueueMessage(msg, uptimeMillis);
}

Handler#dispatchMessage的處理

dispatchMessage是Handler的方法,就是用來分發處理訊息的,直接看原始碼


    /**
     * 分發訊息
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
        	//交由Message處理,通過Handler的post方法傳送的訊息Runnable會賦值給msg的callback屬性
            handleCallback(msg);
        } else {
        	//交由handler的Callback處理
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                	//如果Callback的handleMessage方法返回true,那麼dispatchMessage方法將結束,意味著下面的Handler的handleMessage方法將不被回撥.
                    return;
                }
            }
	      /*如果mCallback==null,或者Callback的handleMessage方法返回false的話,Handler的
	      handleMessage才可以接受到訊息.通過Handler的無參構造方法建立Handler的話mCallback就是等於null的.
	      因此我們重寫Handler的handleMessage方法就可以接收到我們傳送的Message物件*/
	       handleMessage(msg);
        }
    }
 	//回撥給我們處理自己的訊息
 	public void handleMessage(Message msg) {
    }
 	
 	//直接處理Message內的Runnable中的run方法
	private static void handleCallback(Message message) {
	    message.callback.run();
	}

分析Handler的事件分發機制可知,當Handler收到訊息開分發的時候優先會判斷Message中是否有繫結Runnable物件,如果有的話,直接執行run方法的邏輯,否則繼續判斷Handler中是否有繫結Callback物件,如果有則執行Callback的handleMessage方法,並判斷其返回值,如果返回true則分發結束,返回false,則會執行我們重寫Handler的handleMessage方法.


總結

通過上面的Handler,Looper,Message的原始碼分析,大概的結論是:
1.Handler通過傳送和處理Message
2. Looper通過輪詢MessageQueue無限迴圈輪詢Message,同時將Message回傳給對應的Handler處理
3. 建立Handler必須要呼叫Looper.prepare()方法去初始化Looper,否則將報異常,主執行緒的Looper是由android系統去呼叫Looper.prepare()的;
4. Looper的loop()方法將是開啟訊息輪詢的關鍵方法,非UI執行緒必須


擴充套件

Handler#post方法分析

post方法會將Runnable物件新增到message queue中,Runnable中的run方法最終是在什麼執行緒中執行是要看建立該Handler所在的執行緒,如果是在主執行緒中建立的Handler,那麼該run方法就會在主執行緒中執行,否則是在子執行緒中執行.
返回值表示該Runnable是否成功的新增到了訊息佇列中

public final boolean post(Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
}

getPostMessage做了什麼操作呢?
很簡單,就是建立一個Message,然後將Runnable物件賦值給它的callback 屬性.

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

接著就和Handler#sendMessage方法一樣了.將訊息傳送到Looper中的MessageQueue中.具體是通過上面提到的enqueueMessage方法.

主執行緒的Looper在哪裡建立

Activity的啟動一般會呼叫到ActivityThread,裡面有main方法,是初始化activity必經階段,主執行緒的Looper建立和呼叫輪詢都是在ActivityThread的main方法中執行的

public static void main(String[] args) {
    SamplingProfilerIntegration.start();
    // CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.
    CloseGuard.setEnabled(false);
    Environment.initForCurrentUser();
    // Set the reporter for event logging in libcore
    EventLogger.setReporter(new EventLoggingReporter());
    Security.addProvider(new AndroidKeyStoreProvider());
    Process.setArgV0("<pre-initialized>");
    
    //1.建立主執行緒的Looper
    Looper.prepareMainLooper();
   
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
	
	//2.建立主執行緒的Handler
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    AsyncTask.init();
    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
	
	//3.開啟輪詢
    Looper.loop();
    
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

主執行緒中的Looper.loop()一直無限迴圈為什麼不會造成ANR?

分析Looper的loop原始碼的時候可以知道MessageQueue是通過next()方法去獲取Message的,而next()方法又是一個阻塞的方法,所以主執行緒是阻塞的,因此CPU並不會消耗太多資源在主執行緒中,像我們平時自己new 的執行緒一樣,如果run方法裡面沒有阻塞的語句,那麼很快就得結束回收掉了.

既然主執行緒阻塞了,那麼為什麼還能呼叫各種生命週期呢?
呼叫生命週期是因為有Looper,有MessageQueue,還有溝通的橋樑Handler,通過IPC機制呼叫Handler傳送各種訊息,儲存到MessageQueue中,然後在主執行緒中的Looper提取了訊息,並在主執行緒中呼叫Handler的方法去處理訊息.最終完成各種生命週期方法的呼叫.

下面貼出主執行緒的Handler的handleMessage方法的處理程式碼:

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
            ActivityClientRecord r = (ActivityClientRecord)msg.obj;
            r.packageInfo = getPackageInfoNoCheck(
                    r.activityInfo.applicationInfo, r.compatInfo);
            handleLaunchActivity(r, null);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        } break;
        case RELAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
            ActivityClientRecord r = (ActivityClientRecord)msg.obj;
            handleRelaunchActivity(r);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        } break;
        case PAUSE_ACTIVITY:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
            handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
            maybeSnapshot();
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        case PAUSE_ACTIVITY_FINISHING:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
            handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        case STOP_ACTIVITY_SHOW:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
            handleStopActivity((IBinder)msg.obj, true, msg.arg2);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        case STOP_ACTIVITY_HIDE:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
            handleStopActivity((IBinder)msg.obj, false, msg.arg2);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        case SHOW_WINDOW:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
            handleWindowVisibility((IBinder)msg.obj, true);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        case HIDE_WINDOW:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityHideWindow")