1. 程式人生 > >詳解Handler和Looper的關係

詳解Handler和Looper的關係

這幾天在看android的Handler和Looper的用法,需然以前對於這兩者的用法我已經很清楚了,但今天我本著深入瞭解的態度去關於兩者的原始碼。果不其然,裡面真是大有文章啊,讓我學到了很多東西,刻不容緩,於是馬上寫部落格記錄下來。。。(看來以後要多看點原始碼咯~~~·生氣)。

在例子開始前,我們來看一下google API對兩者的解釋:

先來看一下Looper這個類:


再來看一下handler這個類的google給出的解釋:

簡單的來說:大家可以這樣來理解,有一個訊息佇列(佇列應該大家都知道了吧。。。。。),我們handler要做的工作就是將一個Message物件都到這個訊息佇列和負責處理從被訊息佇列拿出來的Message,那麼誰負責遍歷和拿取訊息佇列內的訊息給handler呢?沒錯,這些遍歷和拿取的工作就是Looper來完成的。好了,下面來看一下我寫的例子(如果有大神飄過的話,別見怪。。別見怪。。。偷笑

)

先來看一下整個專案的搭構:

其中的佈局檔案main.xml裡面很簡單,久一個TextView和一個Button。(不信的話。。。。。我也不給你看大笑)

好了,現在我們來看一下activity裡面的程式碼

public class HLActivity extends Activity {
    /** Called when the activity is first created. */
	private TextView textView=null;
	private Button btn=null;
	//private Handler handler=null;
	public static int i=0;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initralWeight();
        /*handler=new Handler(){
        	@Override
        	public void handleMessage(Message msg) {
        		// TODO Auto-generated method stub
        		textView.setText("當前點選執行執行緒的次數為"+msg.arg1);
        	}
        };*/
        
    }
    private class BtnListener implements OnClickListener{

		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			
			TestThread tt=new TestThread();
			Thread t=new Thread(tt);
			t.start();
		}
    	
    }
	private class TestThread implements Runnable{
    	private Handler handler=null;
		@Override
		public void run() {
			// TODO Auto-generated method stub
			Looper.prepare();
			handler=new Handler(){
				@Override
				public void handleMessage(Message msg) {
					// TODO Auto-generated method stub
					System.out.println("當前的數字為:"+msg.arg1);
				}
			};
			while(i<10){
				Message msg=new Message();
				msg.arg1=i++;
				handler.sendMessage(msg);
			}
			Looper.loop();
		}
    	
    }
    private void initralWeight(){
    	textView=(TextView) findViewById(R.id.text);
    	btn=(Button) findViewById(R.id.btn);
    	btn.setOnClickListener(new BtnListener());
    }
}
功能很簡單,當點選Button時,就會開啟執行緒,而這個執行緒就會不斷的i++,然後生成個Message物件,將其投遞到訊息佇列裡面去。然後
handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
	     // TODO Auto-generated method stub
	     System.out.println("當前的數字為:"+msg.arg1);
	}
};
這部分的程式碼就是負責處理從訊息佇列內拿取的訊息,那麼這裡又有人問了,
Looper.prepare();
Looper.loop();
到底是有什麼用的?

還有,你說的訊息佇列到底在哪裡?為什麼我沒看見?

別急,下面我們來看一下關於Looper的原始碼:

public class Looper {
    // 每個執行緒中的Looper物件其實是一個ThreadLocal,即執行緒本地儲存(TLS)物件
    private static final ThreadLocal sThreadLocal = new ThreadLocal();
    // Looper內的訊息佇列
    final MessageQueue mQueue;
    // 當前執行緒
    Thread mThread;
    // 。。。其他屬性

    // 每個Looper物件中有它的訊息佇列,和它所屬的執行緒
    private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

    // 我們呼叫該方法會在呼叫執行緒的TLS中建立Looper物件
    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            // 試圖在有Looper的執行緒中再次建立Looper將丟擲異常
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
    // 其他方法
}

大家在這裡可以看到Looper的建構函式Looper()被宣告為private,也就是說,在外部,我們不能直接的使用Looper的建構函式,那麼我們該怎樣去建立一個Looper物件呢?
 public static final void prepare() {
        if (sThreadLocal.get() != null) {
            // 試圖在有Looper的執行緒中再次建立Looper將丟擲異常
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
prepare()這個函式為我們提供了初始化Looper物件的功能,在這裡不瞭解ThreadLocal是什麼的可以去看一下我上一篇部落格,就是介紹ThreadLocal,好了在這裡我不多說了。。。,而這個函式我們要注意它給我們什麼資訊了。很顯然,它告訴我,們只要你呼叫這個方法,那麼系統就會在ThreadLocal內新增一個Looper物件,並且一個執行緒只能有一個Looper,若超出一個,則會丟擲異常。。。。。。

好了,下面是重點:

 public static final void loop() {
        Looper me = myLooper();  //得到當前執行緒Looper
        MessageQueue queue = me.mQueue;  //得到當前looper的MQ
        
        // 這兩行沒看懂= = 不過不影響理解
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        // 開始迴圈
        while (true) {
            Message msg = queue.next(); // 取出message
            if (msg != null) {
                if (msg.target == null) {
                    // message沒有target為結束訊號,退出迴圈
                    return;
                }
                // 日誌。。。
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                // 非常重要!將真正的處理工作交給message的target,即後面要講的handler
                msg.target.dispatchMessage(msg);
                // 還是日誌。。。
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                
                // 下面沒看懂,同樣不影響理解
                final long newIdent = Binder.clearCallingIdentity();
                if (ident != newIdent) {
                    Log.wtf("Looper", "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);
                }
                // 回收message資源
                msg.recycle();
            }
        }
    }
當我們呼叫Looper.loop()這個方法後,Looper才真正的開始工作
Looper me = myLooper();  //得到當前執行緒Looper
MessageQueue queue = me.mQueue;  //得到當前looper的MQ
獲取到當前Looper的MessageQueue,每一個Looper都有一個訊息佇列,並且這個訊息佇列作為Looper的一個成員屬性。

然後再while內迴圈這個訊息佇列,不斷的取出資料

Message msg = queue.next(); // 取出message
先判斷該訊息佇列是否到底了。。。
if (msg != null)
if (msg.target == null) {
      // message沒有target為結束訊號,退出迴圈
      return;
}
接著便是一系列的列印日誌:
// 日誌。。。
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
在下面便是這個方法的核心了。
msg.target.dispatchMessage(msg);
這個方法實際作用便是將Message傳遞給Handler類來處理,而Handler使用
handleMessage
方法來接受,並且處理該資訊。

有些人說了

dispatchMessage
是Handler的方法吧?別急。

下面我們來看一下dispatchMessage這個方法的內部實現:

// 處理訊息,該方法由looper呼叫public void dispatchMessage(Message msg) { if (msg.callback !=null) { // 如果message設定了callback,即runnable訊息,處理callback! handleCallback(msg); } else { // 如果handler本身設定了callback,則執行callbackif (mCallback !=null) { /* 這種方法允許讓activity等來實現Handler.Callback介面,避免了自己編寫handler重寫handleMessage方法。見http://alex-yang-xiansoftware-com.iteye.com/blog/850865 */if (mCallback.handleMessage(msg)) { return; } } // 如果message沒有callback,則呼叫handler的鉤子方法handleMessage handleMessage(msg); } }

可以看到了吧,在dispatchMessage這個方法內有這麼的一句程式碼:
 handleMessage(msg);
呵呵呵呵呵呵~~~~~我想現在大家都應該明白了吧,Handler與Looper兩者之間的關係和呼叫。。。。。。