學習android到今天也將近3個月了,雖然各類界面各類組件一直在歡快的用着,但有一天突然發現,一些簡單的東西去不是很理解:好比Bundle和Handler。因而就抽空好好閱讀了下adk文檔。如今也算是理解一二了,技術須要分享,因而乎花了半天的時間整理了這篇文章。html
這應該是任何一個學習android的人建立的第一個hello world程序都會遇到的一個類,它是activity的oncreate的入參,但因爲作東西的時候不多會遇到它,也就一直沒在乎。java
這個類很簡單,先聽聽官方的解釋:「A mapping from String values to various Parcelable types.」android
Soga,原來就是一個String - Object的映射表,同時又是支持序列化的。windows
再看代碼:「final class Bundle implements Parcelable, Cloneable 」。實現了兩個接口,前者是序列化接口,後置是淺拷貝和深拷貝的接口,具體實現就再也不說了,這個不是重點。網絡
總之一句話,Bundle就是爲了實現數據傳輸而生的,好比activity之間。一般Bundle會附在intent裏面intent.putExtra()。app
好了,打住,進入下一個議題:消息循環!異步
作過客戶端的確定都會接觸到一個概念:UI線程。說白了,UI線程和普通工做線程的區別就在於消息循環。消息循環就是用來處理分發相關線程消息隊列中的消息,Android和Windows都是如此。做爲一名曾經的windows開發人員,研究的第一個點也是系統對UI的消息處理流程,否則幹活都沒底氣。ide
int _WinMain(args ...) { //WND definition ... ... while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { ... ... } }
上面就是通過簡化後的一個最基本的windows的建立窗口的過程。其中main函數裏面的while就是一個消息循環,來不斷從消息隊列裏面讀取消息並dispatch消息。dispatch的消息均會進入到消息處理回調函數WndProc中完成消息的處理。這個程序的main線程也就是一般說的UI線程。假如咱們另起一個線程包含了上面的while循環,則那個線程也照樣能夠建立窗口什麼的,那麼它也是一個UI線程。
函數
若是對win32的ui感興趣,能夠參考http://blog.csdn.net/cheneywong/article/details/8847810oop
上面說了這麼些的windows相關技術,其實就是想描述下窗口處理的基本原理,由於有些概念在windows上更易懂寫,這樣再看android的時候容易理解些。
官方解釋:Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare()
in the thread that is to run the loop, and then loop()
to have it process messages until the loop is stopped.
很明白,looper就是一個用來處理消息的消息循環。默認的Thread也是不具備處理消息的能力,須要添加Looper來實現。說白了,android中的UI線程也就是帶有一個Looper的Thread,用戶全部的操做產生的消息均經過Looper來分發處理(這句沒有看代碼驗證,但應該也是八九不離十了)。
和上一節比較咱們發現好像少了一點東西 -- 消息處理回調函數WndProc。
「Most interaction with a message loop is through the Handler
class.」
這句話也引出了Looper和Hanlder的關係了。一個消息循環分發消息,一個消息處理。以下一段實例代碼,演示了線程中添加消息循環並處理的過程:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
看了這段代碼,我就馬上產生了一個疑問,windows中的wndproc是須要註冊才能進入處理的,那上段代碼handler沒有註冊卻能接收並處理消息!!
官方解釋:A Handler allows you to send and process Message
and Runnable objects associated with a thread's MessageQueue
. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
簡單翻譯:Handler容許你向線程的MessageQueue發送消息,同時還可以處理該消息隊列裏的Message和Runnable。每一個handler只能關聯一個線程,並在建立的時候和線程綁定。
原來建立Hanlder的時候,它會本身綁定到線程和消息隊列上,這就解釋了上一節的疑問了。
另外值得注意的一條規則:每一個Handler只會和一個Thread綁定,即建立它的Thread。後面會講到這條規則的用處。
這裏面又引入了兩個概念:Message和向消息隊列發送消息。
消息體,查看源碼大體包含了以下幾個核心成員:
Bundle date ; // 消息體包含的數據
Handler target; // 消息對應的「target", 即由誰來處理
Runable callback; // 消息處理的回調
Message next; // 下一條消息
Looper的結構相對簡單,主要有如下兩個成員
{
final MessageQueue mQueue;
final Thread mThread;
}
經過成員變量,咱們能夠看出一個消息循環Looper均和一個Thread相關聯,同時還會附帶一個消息隊列MessageQueue。
另外,還有兩個靜態成員:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
該對象能夠理解成管理Thread和Looper的關係就行了。
private static Looper sMainLooper;
主線程的Looper。這樣,任何就能夠在任何線程獲取到主線程(UI線程)的消息循環體了,進而能夠向UI線程發送消息。
最後,咱們再看下以前代碼中出現的Looper.prepare()和Looper.loop()。這兩個函數均是靜態函數
prepare()
經過ThreadLocal建立Looper。
loop():
簡化後的僞代碼大體以下:
public static void loop() { loop = getThisThreadLooper(); queue = loop.mQueue; for(;;){ msg = queue.next(); // 分發消息 msg.target.dispatchMessage(msg); msg.recycle(); } }
原來loop函數也就是一個無線循環,不斷的從消息隊列裏面取出消息並交給Message中的目標Handler處理。好了,這就清晰了,消息循環原來如此簡單。
附加: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(); } }
該函數顧名思義,就是來準備主線程(UI線程)的消息循環,代碼的註釋中也說明了:該函數是初始化application的主looper。該函數有android系統來調用,咱們不該該本身來調用,否則會拋出異常。
上一節中出現了一個函數Handler.dispatchMessage(Message). 下面咱們也會重點分析該函數的。
{ final MessageQueue mQueue; final Looper mLooper; final Callback mCallback; }
Handler中的成員主要有上面3個,消息隊列、消息循環和回調Callback。
(1)初始化
獲取建立本身的線程的消息循環以及消息隊列:mLooper = Looper.myLooper();
另外用戶能夠爲Handler指定一個Callback 。
(2)Callback 接口和handleMessage成員方法
Handler內部定義了以下接口:
public interface Callback { public boolean handleMessage(Message msg); // 返回值意思就是callback處理完以後是否還須要繼續處理,true就是已經處理過了,不須要再進一步處理 }
另外值得注意的是Handler本身還有一個成員函數
public void handleMessage(Message msg) { }
擦,你這是要鬧哪樣!!
查看下代碼的註釋:
Callback:」Callback interface you can use when instantiating a Handler to avoid having to implement your own subclass of Handler.「
handleMessage成員函數:」Subclasses must implement this to receive messages.「
這樣就水落石出了:成員函數主要用在用戶本身繼承Handler實現時來處理message的,而Callback接口則是爲了用戶直接建立Handler時可以處理message用的,而沒必要經過子類實現。
(3)dispatchMessage
Message中有一個Runnable的Callback成員,如今Handler中也有一個Callback,另外Handler還有一個成員函數handleMessage(Message),哇塞,暈掉了有木有,這三個必需要有一個執行次序。代碼以下:
public void dispatchMessage(Message msg) { if (msg.callback != null) { msg.callback.run(); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
即優先級爲:1. 消息體中的Runnable; 2. 若是爲空則執行建立handler時指定的Callback;3. 若是沒指定或者返回值爲false的話就執行handler的handleMessage方法。
至此,android中線程的消息循環和消息隊列就說明的差很少了。
在SDK文檔中的描述Handler,其有兩大主要用處:其一,安排message和runnable在某一時間點執行;其二,向其餘線程的消息隊列中發送消息(好比UI主線程)。
其中第二項功能就是咱們一般用來在異步線程和UI線程打交道的方法了。這主要經過Handler提供的send和post系列方法完成。send對應message,post對應Runnable。
以下:
每一個方法的具體含義請自行查看SDK文檔。這裏仍是要解釋下post的實現:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { // exception return false; } return enqueueMessage(queue, msg, uptimeMillis); }
原來post也是將Runnable轉換成一個message(還記得Message有一個Runnable成員嗎),而後調用send的delay方法。最終的sendMessage也只是將消息壓入消息隊列中。
注意:這裏面的post和send含義和Windows應用程序的post和send的含義是不一樣的。
(1)線程內
上面的分析,咱們就能夠在線程內向handler發送自定義message或post一個runnable。
(2)線程間
以前咱們說過:一個Handler只能和建立它的線程綁定在一塊兒,這樣咱們就能夠在主線程建立一個Handler,而後再工做線程中向這個Handler 進行send 或post操做,這樣建立的message均會加入到UI線程的消息隊列中並被執行,這也就實現了UI線程和普通工做線程的通訊。以下一個代碼示例:
class MyActivity{ Handler handler; ... ... void init(){ handler = new Handler() { @Override public void handleMessage(Message msg) { //處理消息:將網絡獲取的數據在UI上表現出來 ... ... } }; } void getDataFormNet(){ Thread thread = new Thread(new Runnable() { @Override public void run() { //網絡操做 ... ... handler.sendMessage(new Message()); } }); thread.start(); } }
OVER了,學習時間不長,若有錯誤請指正~~