普通線程是一次性的,執行結束後也就退出了(這種說法可能不嚴謹,但爲了下文描述方便)。
但某些狀況下須要無限循環、不退出的線程。好比處理用戶交互的線程,它等待並執行用戶的點擊、滑動等等操做事件,也執行由系統觸發的廣播等事件,稱之爲主線程,也叫UI線程。html
關於這種無限循環線程須要說的是:java
每一個事件及其所包含的信息被封裝爲一個Message
(下文中提到的「消息」和「事件」是一回事兒);android
線程不能同時處理全部事件,因此須要有個收件箱MessageQueue
(下文中提到的線程,若是沒有特別說明是一次性線程,那麼都指looper thread);spring
之因此不一樣於一次性線程,而能無限循環,是Looper
的功勞,因此這種線程也被稱爲message looper thread;編程
事件由Handler
發送和處理。app
Android提供的類HandlerThread
,是一個looper thread類。經過源碼理解它是怎麼辦到的:
HandlerThread源碼連接異步
23 public class HandlerThread extends Thread { 51 @Override 52 public void run() { // 初始化當前線程爲looper thread,prepare()方法保證爲每一個線程只新建一個Looper object; // 在新建Looper object時,新建了一個MessageQueue object; // 因此,HandlerThread對象、Looper對象、MessageQueue對象,三者之間創建了彼此唯一的關聯。 54 Looper.prepare(); 55 synchronized (this) { // 該靜態方法返回與當前線程關聯的Looper object,若是當前線程未關聯Looper則返回空; // 稍後講到的Handler也是經過該方法創建和當前線程的唯一關聯; // 至於爲何是當前線程,好像是和ThreadLocal<Looper>這個靜態變量有關,不甚明瞭,存疑 TODO. 56 mLooper = Looper.myLooper(); 57 notifyAll(); 58 } // 你能夠覆寫這個函數,以在循環開始前作些必要的工做,好比實例化一個Handler,稍後講它。 60 onLooperPrepared(); // 調用loop()方法後,Looper對象就開始循環地從MessageQueue對象中取消息、處理消息、沒消息時等待消息; // 這就使得它成爲了一個looper thread; // 而如何處理消息,涉及Handler,稍後講它。 61 Looper.loop(); 63 }
因此,建立一個HandlerThread實例,就建立了一個looper thread:async
// 在合適的地方新建並啓動線程,好比在Activity.onCreate()方法。 HandlerThread mHandlerThread = new HandlerThread("Gallery Downloading Thread"); mHandlerThread.start(); // 必須調用getLooper(),並且必須在start()以後調用,該方法獲取與線程關聯的Looper對象,以確保線程就緒; // 由於該方法會被阻塞直至looper對象初始化好了。 mHandlerThread.getLooper(); // 在合適的地方調用quit()方法,好比Activity.onDestroy()方法,以退出線程的looper循環,再也不處理消息隊列中的任何消息; // 此處存疑 TODO:線程退出了嗎?(好像並無,該如何處理?) mHandlerThread.quit();
如此,一個looper thread就在運轉了,但它發現沒有消息,因而它等待。問題是,
該如何傳遞消息給它;它拿到消息後又是怎麼處理的呢?答案在Handler。ide
關於Handler須要說的是:
Handler使你可以發送並處理Message
和Runnable
對象;
當你建立了一個Handler實例,它就被綁定給建立它的線程,只能綁定給這1個線程,這也就意味着只關聯了1個MessageQueue
,此後,就能夠發送messages和runnables給所關聯的message queue,而當這些消息出隊時,就在handler綁定的線程中獲得執行;
Handler主要有2種用途:(1) 安排messages和runnables在未來的某個時間點執行;(2) 讓另外一個線程作些事情。
以上內容截取自源碼文件的註釋(翻譯後)。
Handler mHandler = new Handler();
經過源碼來理解爲何handler被綁定到了建立它的線程:
188 public Handler(Callback callback, boolean async) { // 經過該靜態方法得到與當前線程綁定的Looper,而Looper是和當前線程唯一關聯的(參看上文描述); // 這樣就創建了handler和當前線程及其MessageQueue的唯一關聯; // 固然也能夠經過另外一個方法關聯到指定的Looper,此處暫且不表 TODO. 198 mLooper = Looper.myLooper(); // 若是當前線程沒有looper,就沒有queue,就無法接收消息,因此拋出異常。 199 if (mLooper == null) { 200 throw new RuntimeException( 201 "Can't create handler inside thread that has not called Looper.prepare()"); 202 } 203 mQueue = mLooper.mQueue; 204 mCallback = callback; 206 } // 默認的構造器就調用上面的方法,綁定handler到當前線程,也就是建立它的線程。 113 public Handler() { 114 this(null, false); 115 }
如今有了handler,又該如何發送消息呢?在發送以前,咱們先新建一個消息。
Message msg = new Message(); msg.what // 自定義的消息識別碼,整形,以便接收者識別消息; msg.arg1 // 若是兩個int數據就能夠知足你的要求,就用arg1和arg2; msg.arg2 msg.obj // 若是複雜,就傳遞Object對象,或者Bundle數據; msg.setData(Bundle data)
Handler能夠發送處理Message
和Runnable
兩種對象,提供了若干方法:
boolean post(Runnable r); boolean postAtTime(Runnable r, Object token, long uptimeMillis); boolean postDelayed(Runnable r, long delayMillis); boolean sendEmptyMessage(int what); boolean sendMessage(Message msg); boolean sendMessageAtTime(Message msg, long uptimeMillis); boolean sendMessageDelayed(Message msg, long delayMillis);
這些方法最終都調用了sendMessageAtTime()
,而後把消息放入隊列。Runnable對象在內部被轉成了Message對象的callback
字段,稍後講它。
須要特別關注message的成員變量Handler target
,消息入隊時,該字段被設置爲發送它的handler,這就確保了message對象和handler對象的唯一關聯。這樣在message出隊時,就知道該交給哪一個handler處理;除此以外它還有別的用處,稍後講它。
上文發送消息的作法,每次都須要新建一個message實例。若是頻繁的話,Android推薦這樣作:
mHandler .obtainMessage(int what, int arg1, int arg2, Object obj) .sendToTarget();
Handler.obtainMessage(...)
方法從公共循環池裏獲取消息(若是沒有的話,它會建立新的實例),並傳入消息的各個字段。這樣能夠避免建立新的Message實例,提升效率。Message.sendToTarget()
方法只執行了一條語句target.sendMessage(this)
,這就是上文提到的target
字段的別的用處。這樣就把消息放入了隊列。
這是消息循環的核心源碼,一個死循環:
// Looper.loop() 109 public static void loop() { 110 final Looper me = myLooper(); 114 final MessageQueue queue = me.mQueue; 115 121 for (;;) { 122 Message msg = queue.next(); // might block 沒有消息則阻塞。 123 if (msg == null) { // 收到空消息就退出。 124 // No message indicates that the message queue is quitting. 125 return; 126 } // 這就是前面提到的target字段的做用:知道把出隊的消息交給誰。 135 msg.target.dispatchMessage(msg); 153 } 154 }
下面就開始dispatchMessage:
// Handler.dispatchMessage() 93 public void dispatchMessage(Message msg) { // 上文提到的,發送消息時,Runnable對象在內部被轉成了Message對象的`callback`字段; // 若callback不爲空,那麼消息就是一個runnable對象,那麼就執行它; 94 if (msg.callback != null) { 95 handleCallback(msg); 96 } else { // 若是不是runnable,那麼就是Message對象了: // 上文只講了使用默認構造器建立handler,也能夠Handler(Callback callback); // 這樣就爲每一個handler設置了各自的callback,優先執行它; 97 if (mCallback != null) { 98 if (mCallback.handleMessage(msg)) { 99 return; 100 } 101 } // 若是handler對象沒有本身的callback,那麼就執行這個方法; // 這是一個空方法,當經過默認構造器新建一個handler時須要覆寫它。 102 handleMessage(msg); 103 } 104 }
理論知識已經具有了,那麼接下來,在1個典型的應用場景中使用它們解決咱們的問題。
這段代碼截取自《Android權威編程指南》第26, 27章,做者構建了一個這樣的App:下載Flicker上最新的100張縮略圖,並填充到GridView內。首先經過AsyncTask線程獲取包含縮略圖url信息的XM文件;而後下載那些url指向的縮略圖。這裏須要思考的是,什麼時候下載這些縮略圖,是一次下載完?仍是下載一部分?由誰觸發下載?下載後怎樣填充到GridView內?
解決了這些問題,就基本掌握了建立後臺線程,並和主線程通訊的方法。
public class PhotoGalleryFragment extends Fragment { ThumbnailDownloader<ImageView> mThumbnailDownloader; @Override public void onCreate(Bundle savedInstanceState) { mThumbnailDownloader = new ThumbnailDownloader<ImageView>(new Handler()); mThumbnailDownloader.setOnThumbnailDownloadListener(new OnThumbnailDownloadListener<ImageView>() { public void onThumbnailDownloaded(ImageView imageView, Bitmap bitmap) { imageView.setImageBitmap(bitmap); } }); mThumbnailDownloader.start(); mThumbnailDownloader.getLooper(); } @Override public void onDestroy() { mThumbnailDownloader.quit(); } private class GalleryItemAdapter extends ArrayAdapter<GalleryItem> { @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView; String url; // 對於須要顯示在GridView視圖中的成百上千張圖片,咱們不可能一次下載完而後顯示; // 只能在須要顯示時下載,而GridView的adapter知道何時顯示哪些視圖; // 因此咱們在此處安排後臺線程的下載任務。 mThumbnailDownloader.queueThumbnail(imageView, url); return convertView; } } } public class ThumbnailDownloader<Token> extends HandlerThread { private static final String TAG = "ThumbnailDownloader"; private static final int MESSAGE_DOWNLOAD = 0; Handler mHandler; Map<Token, String> requestMap = Collections.synchronizedMap(new HashMap<Token, String>()); Handler mResponseHandler; OnThumbnailDownloadListener<Token> mOnThumbnailDownloadListener; public interface OnThumbnailDownloadListener<Token> { void onThumbnailDownloaded(Token token, Bitmap thumbnail); } public void setOnThumbnailDownloadListener(OnThumbnailDownloadListener<Token> l) { mOnThumbnailDownloadListener = l; } public ThumbnailDownloader(Handler responseHandler) { super(TAG); // 後臺線程能在主線程上完成任務的一種方式是,讓主線程將其自身的Handler傳給後臺線程; // mResponseHandler始終和主線程保持關聯,由它發送的消息都將在主線程中獲得處理。 mResponseHandler = responseHandler; // 咱們也能夠傳遞主線程的context,經過下述方式獲取主線程的handler: // mResponseHandler = new Handler(mContext.getMainLooper()); } public void queueThumbnail(Token token, String url) { // requestMap是一個同步HashMap。 使用Token做爲key,可存儲或獲取與特定Token關聯的URL. requestMap.put(token, url); // mHandler是和後臺線程關聯的,咱們開放這個方法給主線程,主線程調用這個方法來安排後臺線程的任務。 // 咱們把下載信息封裝成message後放入後臺線程的收件箱。 mHandler.obtainMessage(MESSAGE_DOWNLOAD, token).sendToTarget(); } @Override protected void onLooperPrepared() { // onLooperPrepared()方法發生在Looper.loop()以前,此時消息尚未開始循環, // 因此是咱們實現mHandler的好地方,在此處下載。 mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == MESSAGE_DOWNLOAD) { Token token = (Token)msg.obj; handleRequest(token); } } }; } private void handleRequest(final Token token) { // 下載,並根據獲取的數據構建Bitmap對象; final String url = requestMap.get(token); final Bitmap bitmap; // 下載完成後,咱們在後臺線程使用與主線程關聯的handler,安排要在主線程上完成的任務。 // 除了post,咱們也能夠sendMessage給主線程,那麼主線程的handler須要覆寫本身的handleMessage()方法。 mResponseHandler.post(new Runnable() { @Override public void run() { if (mOnThumbnailDownloadListener != null) { mOnThumbnailDownloadListener.onThumbnailDownloaded(token, bitmap); } } }); } }
強烈建議閱讀《Android權威編程指南》第26, 27這兩章代碼。
本書的官方主頁連接 能夠從這裏獲取本書的源碼
但我在學習這兩個章節的時候,有些概念和類的描述理解很吃力,好比「消息循環由一個線程和一個looper組成。 Looper對象管理着線程的消息隊列」,又好比「一個Handler僅與一個Looper相關聯,一個Message 也僅與一個目標Handler」。
由於做者着重講解app的實現思路,對涉及到的類只給出告終論,沒有說明爲何。因此初學時很費解,實際編程時也是隻知其然不知其因此然。後來讀了些博文(下面給出了連接),又閱讀了源碼,總算釐清了這些類,而這篇文章就是我理解後的一個產物。
下面是3個做者的4篇博文,總結的都很棒,側重於源碼分析。尤爲是第2篇,做者最後畫了一張圖,經過傳送帶來解釋涉及到的類和概念:「在現實生活的生產生活中,存在着各類各樣的傳送帶,傳送帶上面灑滿了各類貨物,傳送帶在發動機滾輪的帶動下一直在向前滾動,不斷有新的貨物放置在傳送帶的一端,貨物在傳送帶的帶動下送到另外一端進行收集處理。」
深刻源碼解析Android中的Handler,Message,MessageQueue,Looper - iSpring的CSDN博客
android的消息處理機制(圖+源碼分析)——Looper,Handler,Message - CodingMyWorld的cnblog
下面就是源碼連接了,對於理解android的消息處理機制很是有幫助。
版權聲明:《Android Message Handler 消息處理機制總結筆記》由 WeiYi.Li 在 2015年10月15日寫做。著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
文章連接:http://li2.me/2015/10/communicate-with-a...