詳解Handler和Looper的關係
這幾天在看android的Handler和Looper的用法,需然以前對於這兩者的用法我已經很清楚了,但今天我本著深入瞭解的態度去關於兩者的原始碼。果不其然,裡面真是大有文章啊,讓我學到了很多東西,刻不容緩,於是馬上寫部落格記錄下來。。。(看來以後要多看點原始碼咯~~~·)。
在例子開始前,我們來看一下google API對兩者的解釋:
先來看一下Looper這個類:
再來看一下handler這個類的google給出的解釋:
簡單的來說:大家可以這樣來理解,有一個訊息佇列(佇列應該大家都知道了吧。。。。。),我們handler要做的工作就是將一個Message物件都到這個訊息佇列和負責處理從被訊息佇列拿出來的Message,那麼誰負責遍歷和拿取訊息佇列內的訊息給handler呢?沒錯,這些遍歷和拿取的工作就是Looper來完成的。好了,下面來看一下我寫的例子(如果有大神飄過的話,別見怪。。別見怪。。。
先來看一下整個專案的搭構:
其中的佈局檔案main.xml裡面很簡單,久一個TextView和一個Button。(不信的話。。。。。我也不給你看)
好了,現在我們來看一下activity裡面的程式碼
功能很簡單,當點選Button時,就會開啟執行緒,而這個執行緒就會不斷的i++,然後生成個Message物件,將其投遞到訊息佇列裡面去。然後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()); } }
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兩者之間的關係和呼叫。。。。。。