android的訊息機制
很多以前掌握的知識,總是慢慢的再忘記,看來還是自己理解的不夠透徹,希望用部落格的形式記錄下來。
說起android的訊息機制,那不得不提Handler,它的中文含義就是控制者,處理者。
安卓上的關於Handler的文檔是這麼寫的:
Handler可以發送和處理Message,Runnable對象,並且與一個線程的MessageQueue關聯。
當建立了Handler執行個體,它就會綁定在建立它線程的訊息佇列上。
首先在主線程中建立Handler的執行個體,與主線程的MessageQueue綁定,MessageQueue裡有一個輪詢者Looper,它會不斷的查看訊息佇列是否有訊息。如果有訊息,就會把這個訊息扔給Handler的handleMessage去處理。
這時,子線程拿到Handler對象,往主線程的MessageQueue發訊息,訊息佇列就有了訊息,就會把訊息扔給handleMessage去處理訊息。
上面的過程,聽起來十分繞耳。為什麼要這麼做?在子線程中去處理不行嗎?
我們知道子線程一般是用於處理耗時的操作,是不可以更新UI的。
因為多線程同時去draw,去操作UI介面,UI介面實在不好控制。操作UI的動作必須要由主線程去做。
所以在子線程中碰見需要更新UI,我們可以使用android為我們提供的Handler。它的出現主要就是為瞭解決在子線程中無法訪問UI的矛盾。
而如果強行的在子線程去操作UI,可能會出現 “ android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.”的異常。
1,new Handler的時候,Handler究竟做了什嗎?
Looper.mylooper()
Looper對象是從ThreadLocal集合中get得到的。那什麼時候,ThreadLocal把Looper對象set進去的呢?
在prepare方法中,建立了LoZ喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcGVyttTP86OssqLM7bzTtb3By7TmtKLXxUxvb3BlcrbUz/O1xFRocmVhZExvY2Fs1tChozxiciAvPg0KPGltZyBhbHQ9"這裡寫圖片描述" src="http://www.bkjia.com/uploads/allimg/160407/04145QQ6-5.png" title="\" />
而在new Looper的同時,同樣也建立了MessageQueue的執行個體。
而調用prepare方法的是prepareMainLooper。
在應用運行時,主線程ActivityThread產生並執行,而在主線程的main方法中,就會調用prepareMainLooper。
也就是說,在應用一啟動,就會去建立Looper,MessageQueue對象。而在Handler的構造方法中,是拿到已經建立好的Looper和MessageQueue。
2,Message訊息對象
new Message可以直接建立訊息對象。
通過handler.obtainMessage(有許多重載的方法)可以建立訊息,翻看源碼可以得知,訊息的建立全都是由obtain方法去處理。
這裡的sPool表示的是訊息池中的第一條訊息,當訊息池中沒有訊息時,就會new一個Message返回。
如果訊息池中有的話,就廢物利用,把訊息池中的訊息返回。
而訊息池中的訊息是由Message本身來維護的,而不是MessageQueue。
訊息池中的訊息是這樣擺放的:
sPool表示是第一條訊息,它指向著a。
Message m=sPool;
這時m也指向了a
sPool=m.next
sPool這時不再指向a,而是指向了下一個訊息b。
m.next=null
sPoolSize--
a的next成員變數不再指向b,置為null。訊息佇列的長度減減,返回訊息池中的訊息。
3,Looper處理訊息的過程
子線程發送訊息後,主線程的MessageQueue就有了訊息。而訊息的扔給Handler去處理,是需要Looper來輪詢的。
定位到Looper的輪詢訊息的loop方法。
Message msg = queue.next(); // might block .......... .......... .......... msg.target.dispatchMessage(msg);
從訊息佇列中取出的訊息交由了msg.target這個成員變數的dispatchMessage方法。
而msg.target是Handler類型的。那msg.target是怎麼傳遞進來的:
public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; }
該handler在建立Message時,賦予給Handler類型的target。
而在dispatchMessage中,會調用handleMessage。
而在主線程中,我們定義的handleMessage就會覆蓋該方法。
所以,Message對象就這樣一步一步的傳遞到了主線程Handler的handleMessage中。
結論就是:訊息是由哪個Handler發送的,就由哪個Handler去處理。