Handler建立的初衷是爲了解決子線程更新UI的問題:當Android程序第一次啓動時,會建立一條(僅一條)UI線程,用於處理與UI相關的事件(就是咱們的Activity);而爲了保證線程安全性,規定只有UI線程能夠修改Activiy中的組件。這就帶來了一個問題,Andorid程序在很大程度上都依賴於UI組件(各線程的處理結果大多都以更新UI組件來呈現),Handler的消息機制就是用於將子線程的結果數據傳遞給UI線程進行更新執行,而UI線程能夠放心地把那些耗時的操做放到子線程中,本身管好本身的UI就好了。固然,Handler沒有規定只用於更新UI,你徹底能夠當他是一個異步的消息處理(在一個線程中發送消息,在另外一個線程中處理接收到的消息),在使用時,你只要遵循它所制定的規則便可。若是有人問爲何要用子線程處理耗時任務,那什麼ANR網上有不少講解。java
既然是消息機制,首先來了解下消息的結構,它由Handler對象進行發送和處理,包含標識信息和任意的數據對象:android
public final class Message implements Parcelable { //消息標識,用戶自定義,用於收發雙方肯定對方身份 public int what; //用於存放簡單的整數數據,效率較高 public int arg1; //同上 public int arg2; //Bundle結構(封裝的ArrayMap),用於存儲數據信息 Bundle data; //控制消息發送、處理,後面詳細分析 public void setData(Bundle data) { this.data = data; } public Bundle getData() { if (data == null) { data = new Bundle(); } return data; } //處理消息的對象,即Handler Handler target; //具體的消息處理,優先級高於Handler的handleMessage,後面詳細分析 Runnable callback; //任意類型的對象數據,當使用Messenger進行進程間消息傳遞時,常常用來存放Parcelable(序列化對象) public Object obj; //基於Message的進程間通訊管理器,將Handler做爲接收者(指定發送的消息由那個Handler處理) public Messenger replyTo; //標識消息是否在使用,用於消息池的回收再用控制 static final int FLAG_IN_USE = 1 << 0; //指向消息池中可用的消息 private static Message sPool; //替代構造函數,從消息池中獲取可用的消息(避免直接構造時的空間申請),當消息池中沒有可用時才調用構造 //一些重載函數(帶Message、Handler、what等參數)再也不復述 public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); } }
能夠簡單地進行歸納下:what標識消息;arg一、arg二、data和obj存儲數據;target和callback控制消息的處理;obtain建立消息(固然經常使用的還有Handler.obtainMessage(),後面會提到);replyTo用於進程間通訊(不在本文探討範圍)。安全
要講解Handler,就不能不說他的死黨Looper。Looper能夠說是Handler處理消息的控制者,而且擁有很是重要的屬性——存儲待處理消息的消息隊列MessageQueue。每個Looper對象在構造之初便會初始化一個消息隊列(一一對應),用於存放它所控制的那些Handler在其餘線程所發送過來的消息。首先看一下Looper的屬性和構造:多線程
public final class Looper { final MessageQueue mQueue; //消息隊列 final Thread mThread; //當前線程標識 //構造函數,將當前線程構建爲Looper線程(初始化線程對應的消息隊列,固然還有初始化線程私有屬性,後面爲講到) private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } }
不對啊,這是要搞事情啊,構造函數怎麼是私有的呢?由於它不想讓你用這個構造,這裏涉及到多線程,Java對多線程的處理大多都給出了重載函數,一方面保證單線程使用的性能,另外一方面保證多線程使用時數據的準確性(同步與異步),因此這裏也給出了相應的封裝函數,看一下具體處理:app
public final class Looper { //ThreadLocal:線程私有屬性,不受其餘線程的影響,用於存儲與同進程中其餘線程不一樣的那些私有數據(帶有存儲私有數據的hash map),這裏主要用於構造控制(見prepare) static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); //Looper構造與初始化函數 public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { //構造Looper對象時初始化其私有屬性,經過私有屬性保證線程只建立一次(一個線程中prepare只被調用一次) if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } }
喲西,這樣咱們在一個線程中調用Looper.prepare(),便可將當前線程構建成Looper線程,同時初始化了Looper線程的消息隊列和私有屬性。而後看一下Looper具體作了哪些工做(爲了便於閱讀,只列出重要的代碼部分):異步
public static void loop() { //獲取當前Looper線程的Looper對象,loop函數在prepare函數以後調用,二者工做在同一線程中 final Looper me = myLooper(); final MessageQueue queue = me.mQueue; for (;;) { //從消息隊列中取出一條未處理的消息 Message msg = queue.next(); if (msg == null) { return; } try { //具體的消息處理過程,而dispatchMessage這個函數是Handler的函數,下面分析Handler時會詳細講述 //這裏的處理也說明了一點:loop函數是在處理線程中調用的 msg.target.dispatchMessage(msg); } //回收處理完的Message資源(將Message的各項值還原成初始化值,而後丟到消息池中) msg.recycleUnchecked(); } }
上面還有一個問題沒有解決,那就是msg.target這個對象是什麼。毫無疑問,他是Handler的對象,由於dispatchMessage是Handler類的函數,而target在這裏的做用就是將發送和處理進行關聯(在發送線程中,將Handler做爲Message的target屬性與Message進行綁定,在處理線程中,經過Message的target對Message進行處理)。這樣就經過屬性target將發送消息和處理消息聯繫起來了,即哪一個Handler對象發送消息,就由哪一個Handler進行處理。async
消息的構造方法有不少,如Message的obtain,Handler的obtainMessage,固然也有人就喜歡直接使用Message的構造函數。Handler的obtainMessage和Message的包含Handler參數的obtain直接在構造時就肯定了他的target,有些則是等到最終的統一確認階段——發送消息時進行target賦值(Handler的sendMessage),下面給出在構造時肯定target的狀況,發送消息時的統一確認在講述Handler時會提到:ide
public class Handler { public final Message obtainMessage() { return Message.obtain(this); } } public final class Message implements Parcelable { public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; } }
講到這,下面看一下Looper如何使用,提供一個最簡單的例子:函數
public class TestLooper extends Thread { //聲明Handler對象,供發送線程使用 public Handler mHandler; public Handler mHandlernew; @Override //必須在線程中,Looper是將尋常線程初始化爲Looper線程,且不能在UI線程中 public void run() { //初始化,將TestLooper轉化成Looper線程 Looper.prepare(); //定義Handler對象,即將Handler對象與Looper綁定,使得消息隊列中的Message經過其target能夠找到正確的Handler進行處理,能夠定義多個不一樣Handler類的對象。固然,若是你在一個Looper下定義同一個Handler類的多個對象,我也布吉島結果是啥 Handler mHandler = new MyHandler(){ @Override public void handleMessage(Message msg) {...} }; Handler mHandlernew = new MyHandlernew(){......} //開始循環處理消息 Looper.loop(); } }
上面的TestLooper類中,咱們首先構造了一個Looper線程TestLooper;隨後定義了兩個Handler對象,並重寫了他們的handleMessage函數(必須);最後調用loop開始等待、處理消息。在使用時,只需定義TestLooper的對象,調用其start方法便可啓動線程,以後能夠手動調用quit或發送信號量結束該Looper線程,到這裏Looper的工做就完成了(UI線程中直接定義Handler對象便可)。在發送線程中,經過Handler對象的引用(Handler對象做爲參數傳遞給發送線程)或將Handler做爲Looper對象的屬性(在發送線程中定義處理線程的對象,經過對象調用其屬性Handler)等方法發送消息,在結束Handler講解後會有使用的代碼,這裏給出簡易的圖示說明Looper的功能:oop
不能在UI線程中的緣由:UI線程在建立時就已經設定爲是一個Looper線程,若是在UI線程中調用Looper.prepare(),就至關於屢次建立會拋出異常,下面簡單的看下對UI線程在Looper方面的控制
//控制應用程序的主線程執行和調度 public final class ActivityThread { public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); SamplingProfilerIntegration.start(); ........ Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); } } public final class Looper { //用於將當前線程建立爲Looper線程,且標識爲應用的main Looper,這個函數由Android環境所調用 public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } }
Looper還有一些獲取當前Looper屬性的函數,這裏一併給出:
public final class Looper { //獲取Looper對象對應的線程標識 public @NonNull Thread getThread() { return mThread; } //獲取當前Looper線程的Looper對象 public static @Nullable Looper myLooper() { return sThreadLocal.get(); } //獲取Looper對象對應的消息隊列 public @NonNull MessageQueue getQueue() { return mQueue; } //結束此Looper public void quit() { mQueue.quit(false); } }
終於輪到Handler了,Handler提供了兩個功能:1.將打有本身標識的消息(包括可運行過程)發送給另外一個線程 2.根據打有本身標識的消息在對方線程中執行操做。
能夠打個不是很恰當的比喻,Handler比如是一個外賣餐點,他提供兩個功能:1.點餐,不論是美團的仍是大衆點評的,只要客戶是經過我家點餐頁面點的外賣(特定Handler對象發送消息),那都把這些消息發送給我(Handler綁定的MessageQueue);2.送餐(具體操做),這是餐點在線下(相對於美團這種發送線程,線下就至關因而處理線程,而餐點就是在該線程定義的Handler)執行的,固然有可能會有一些矯情的(我要身高一米八,皮膚白皙笑起來還很陽光的帥哥送,這就是Runnable,由發送的Message決定具體操做,固然執行者仍是餐點)
首先來看一下Handler的屬性和構造函數:
public class Handler { final Looper mLooper; final MessageQueue ; final Callback mCallback; //不帶Looper參數的構造最終走的函數,沒有的參數用null、false代替 public Handler(Callback callback, boolean async) { //獲取當前Looper線程的Looper對象。咱們在使用的時候,Handler的構造函數都是在消息處理線程中執行(定義Handler對象,將Handler對象與Looper線程、消息隊列進行綁定),因此這裏獲取的是消息處理線程的Looper對象,而消息發送線程通常是經過參數傳遞得到Handler對象的引用,經過此引用來發送消息 mLooper = Looper.myLooper(); //當前線程不是Looper線程,拋出異常,即Handler必定要在Looper線程中定義 if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //關聯消息隊列 mQueue = mLooper.mQueue; //相似回調機制,在Activity中實現interface Callback的boolean handleMessage(Message msg)函數,利用此callback建立Handler,能夠代替定義Handler對象時重寫void handleMessage(Message msg),功能相同 mCallback = callback; } //帶Looper參數的構造最終走的函數,沒有的參數用null、false代替 public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } }
能夠看到,Handler的構造作了兩件事:1.綁定Looper(和定義Handler對象所在的Looper(處理消息的Looper)進行綁定,同時綁定Looper對應的消息隊列),目的:綁定消息隊列,這樣無論Handler在其餘哪一個線程中發送消息,都能保證正確的送達其對應的消息隊列 2.若是須要,設置回調函數(代替handleMessage)
下面來看一下他的發送消息,有不少重載函數,sendMessage(Message msg)、sendEmptyMessage(int what)、sendMessageDelayed(Message msg, long delayMillis)、sendEmptyMessageAtTime(int what, long uptimeMillis)等等,他們最終都會調用sendMessageAtTime(Message msg, long uptimeMillis):
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { //將綁定的消息隊列設置成參數傳入 MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //這就是target的統一賦值,將Handler做爲Message的target屬性,具體解析見Looper分析 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //將消息插入到綁定的消息隊列中,這樣處理線程就能正確的接收他所綁定的Handler發送的消息 return queue.enqueueMessage(msg, uptimeMillis); } boolean enqueueMessage(Message msg, long when) { synchronized (this) { //標識消息正在使用,防止此時消息被再次入隊或被回收 msg.markInUse(); msg.when = when; //消息隊列的頭結點 Message p = mMessages; boolean needWake; //隊列爲空,當前消息做爲頭結點 if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; needWake = mBlocked; } //隊列不爲空,當前消息插入到隊尾 else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; prev.next = msg; } if (needWake) { nativeWake(mPtr); } } return true; }
固然還有那個誰——Runnable,他也是以消息的形式進行發送的,固然,加了一層封裝,並將他的需求設置到Message的callback(優先級大於handleMessage),這樣就能控制Handler執行他指定的需求,具體執行稍後講述,先看下他的發送,只列舉post看一下他的封裝,postAtTime(Runnable r, long uptimeMillis)、postDelayed(Runnable r, long delayMillis)等等同理:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); //將Runnable對象設置爲Message的callback屬性,封裝到一個空Message中 m.callback = r; return m; }
這樣,不論是Message仍是Runnable,都只要走Message的統一發送接口,就能把咱們的需求發送處處理線程的消息隊列中。至此,發送線程中的操做就完成了,那要怎麼使用呢,舉個最簡單的例子(接着上面的TestLooper):
TestLooper mLooper = new TestLooper(); //啓動TestLooper線程(啓動Looper、監聽MessageQueue) mLooper.start(); //經過Looper對象綁定的Handler屬性發送消息 Message msg = mLooper.mHandler.obtainMessage(); msg.what = 1; mLooper.mHandler.sendMessage(msg); Runnable rmsg = new MyRunnable(); mLooper.mHandlernew.post(rmsg);
最後來看一下Handler的消息處理,也就是上面loop函數中的msg.target.dispatchMessage:
public void dispatchMessage(Message msg) { //對比getPostMessage可知,這個callback就是Runnable,handleCallback其實就是調用Runnable的run方法,可見Runnable的優先級是最高的,當有特殊要求須要處理線程執行時,可用這個 if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { //回調函數,執行interface Callback的boolean handleMessage(Message msg),優先級次之 if (mCallback.handleMessage(msg)) { return; } } //定義Handler對象時重寫的handleMessage函數,優先級最低,能夠把他當成時最大衆的處理方式,若是沒有特殊需求,處理他,有特殊需求,就用Runnable代替 handleMessage(msg); } }
到這裏,Handler的發送、處理消息也就分析完了,下面看一下如何使用。
上面已經列舉了一個TestLooper的例子,首先將一個線程類定義成一個Looper類(Looper類中定義了Handler對象),調用線程類的start函數使Looper線程開始工做,經過Looper線程對象的Handler屬性發送消息。下面列舉一個經過 消息機制更新UI的例子:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView text = (TextView) findViewById(R.id.editText); //在UI線程中,不用再建立Looper,直接定義Handler final Handler myHandler = new Handler(){ @Override public void handleMessage(Message msg){ if(msg.what == 0x01){ Bundle paramBuldle = msg.getData(); text.setText(paramBuldle.getString("data")); } } }; //啓動發送線程 MyThread mthread = new MyThread(myHandler); mthread.start(); } } public class MyThread extends Thread{ private Handler pHandler; public MyThread(Handler inputHandler){ //處理線程中Handler對象的引用 pHandler = inputHandler; } @Override public void run() { Message msg = myHandler.obtainMessage(); msg.what = 0x01; for(int i = 0; i < input.length; i++){ Bundle paramBuldle = new Bundle(); paramBuldle.putString("data",input[i]); msg.setData(paramBuldle); //經過Handler對象的引用發送消息,由參數傳遞 pHandler.sendMessage(msg); } } }
在調試smali時,常常會遇到消息機制,而消息機制的異步特性致使調試時沒法關聯(sendMessage和handleMessage),這時就要藉助消息機制的特性。
1.調試時遇到sendMessage,沒法肯定消息處理函數的狀況,首先看看找對應的handleMessage:
invoke-virtual {v1, v0}, Landroid/os/Handler;->sendMessage(Landroid/os/Message;)Z
這時根據消息機制的特性——發送消息的Handler也即處理消息的Handler,而這裏時Handler通用類型,那就要回溯去尋找v1的類型
iget-object v1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler;
而後查找Lyi/decoder_yi/Helper/ThreadLooper;->pHandler 這個對象在何處賦值,若是賦值的地方較多,就須要藉助調試幫忙肯定,這裏發現是在初始化中賦值,很顯然是一個參數傳遞引用
.method public constructor <init>(Landroid/os/Handler;)V invoke-direct {p0}, Ljava/lang/Thread;-><init>()V iput-object p1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler;
接着就要查找這個初始化函數是在哪裏被調用,又是用什麼參數進行賦值的(肯定Handler對象),這時若是又有多個,那仍是須要藉助調試,對仍是他(由於在一些接口、虛函數處,咱們沒法用靜態的方法肯定它具體會執行哪個,只有經過動態調試去肯定)
iget-object v3, p0, Lyi/decoder_yi/Activity/AESdecode;->mhandler:Landroid/os/Handler; invoke-direct {v2, v3}, Lyi/decoder_yi/Helper/ThreadLooper;-><init>(Landroid/os/Handler;)V
到這一步,咱們的Handler對象就能夠肯定了,就是這個mhandler,接着咱們就要找到mhandler的具體類型,而handleMessage就在這個具體類型的smali文件中(這個具體類型其實就是Handler的一個子類)
invoke-direct {v2, p0}, Lyi/decoder_yi/Activity/AESdecode$1;-><init>(Lyi/decoder_yi/Activity/AESdecode;)V iput-object v2, p0, Lyi/decoder_yi/Activity/AESdecode;->mhandler:Landroid/os/Handler;
到這就有了,這個類就是 Lyi/decoder_yi/Activity/AESdecode$1,找到它對應的smali文件中的handleMessage,就是所要找的處理函數
2.若是是Runnable,要找處理函數就簡單多了,由於post的參數就是Runnable對象,找到該對象的具體類型(紅色),直接能夠到對應smali文件中查找run方法,這就是處理函數
invoke-direct {v1, p0}, Lyi/decoder_yi/Activity/ongdecode$2$1;-><init>(Lyi/decoder_yi/Activity/ongdecode$2;)V invoke-virtual {v0, v1}, Landroid/os/Handler;->post(Ljava/lang/Runnable;)Z
3.回調函數handleMessage與第一種狀況相似,它尋找的是回調函數所在的類類型(紅色)
invoke-direct {v3, p0}, Lyi/decoder_yi/Activity/activity_desdecode$1;-><init>(Lyi/decoder_yi/Activity/activity_desdecode;)V invoke-direct {v2, v3}, Landroid/os/Handler;-><init>(Landroid/os/Handler$Callback;)V iput-object v2, p0, Lyi/decoder_yi/Activity/activity_desdecode;->mhandler:Landroid/os/Handler;
4.而若是是調試時找到了處理函數,而沒法肯定消息是從哪發送過來的(消息的處理函數由looper控制,調試時是一個死循環,這樣就沒法跳出這個線程去查找發送信息的函數),這種你推比較難理解一點,還好咱們有上面的思路,能夠逆着推導
找到handleMessage後,查看它所在的類,而後查找類的初始化函數調用(這個類就是Handler的子類,咱們的Handler對象的具體類型,而要執行處理函數,就要定義Handler對象,因此確定會有這步初始化工做(這個初始化<init>其實就是類的構造)),發現handleMessage在類 Lyi/decoder_yi/Activity/mappingdecode$1中,緊接着找 Lyi/decoder_yi/Activity/mappingdecode$1;-><init>的調用
invoke-direct {v2, p0}, Lyi/decoder_yi/Activity/mappingdecode$1;-><init>(Lyi/decoder_yi/Activity/mappingdecode;)V iput-object v2, p0, Lyi/decoder_yi/Activity/mappingdecode;->mhandler:Landroid/os/Handler;
這裏v2就是構建的Handler對象,緊接着它被賦值給mappingdecode的屬性mhandler,能夠猜想,發送線程要麼是經過mappingdecode的對象直接操做屬性mhandler發送信息,要麼是將mhandler做爲參數傳遞給發送線程進行信息的發送,不管如何,他會經過mhandler來發送,因此緊跟mhandler的使用
iget-object v3, p0, Lyi/decoder_yi/Activity/mappingdecode;->mhandler:Landroid/os/Handler; invoke-direct {v2, v3}, Lyi/decoder_yi/Helper/ThreadLooper;-><init>(Landroid/os/Handler;)V
顯然這裏是經過傳遞引用的方式來使用這個Handler對象,一樣能夠猜想,在類ThreadLooper中有一個Handler類型的屬性,它做爲 Lyi/decoder_yi/Activity/mappingdecode;->mhandler這個對象的引用,在以後會用這個引用來發送信息
.method public constructor <init>(Landroid/os/Handler;)V invoke-direct {p0}, Ljava/lang/Thread;-><init>()V iput-object p1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler;
pHandler就是這個 引用,接着就跟蹤這個pHandler,看它在哪裏調用了消息發送函數
iget-object v1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler; invoke-virtual {v1, v0}, Landroid/os/Handler;->sendMessage(Landroid/os/Message;)Z
好吧,就是它了(紅色)