衆所周知,Android是不能在主線程進行耗時操作,否則會抛出ANR(應用程式無響應)異常。于是,我們在程式開發中就會廣泛地使用Handler來實作異步消息的處理,如讀取網絡資料并加載到程式的UI界面中。Handler的運作需要MessegeQueue和Looper的支撐,關于其三者的關系,接下來将通過源碼來具體分析。
首先,我們來看一個使用Handler的情況:
public class MainActivity extends Activity {
private Handler handler1, handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler1 = new Handler(); //第一個Handler對象在主線程建立
new Thread(new Runnable() {
@Override
public void run() {
handler2 = new Handler(); //第二個Handler在子線程建立
}
}).start();
}
}
結果:
我們可以看到,出現了一個異常:Can’t create handler inside thread that has not called Looper.prepare()。該異常意思是不能在沒有調用 Looper.prepare()的線程中建立Handler。試着在handler2 = new Handler()前添加一句Looper.prepare(),再次運作,會發現錯誤消失了!
既然,異常來自于Handler,我們可以看一下Handler的構造方法,看看是如何實作的:
//當調用無參構造函數時,會自動調用該方法,即this(null, false);
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == ) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");//異常在此處抛出,mLooper為null
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
明顯,mLooper為null,它是通過mLooper = Looper.myLooper()該語句來指派的,那麼我們有必要看看Looper中的myLooper()這個方法:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
代碼比較簡單,可以看出,mLooper是從ThreadLocal中取出來的(每個線程都保留獨立的變量,關于ThreadLocal,可以參考一下http://blog.csdn.net/lufeng20/article/details/24314381)。那麼sThreadLocal裡的Looper對象在什麼時候添加進去了呢?我們可以看看上面說到的Looper的prepare()方法:
public static void prepare() {
prepare(true);//調用下面重載的prepare()方法
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed)); //為sThreadLocal添加一個Looper對象
}
看到這裡,我們或許可以知道為什麼文章開頭那個代碼片段會出錯了。如果我們不使用Looper.prepare()方法的話,就無法為sThreadLocal添加Looper對象,進而在建立Handler對象時mLooper = null,抛出異常。進一步想,為什麼handler1就不用調用Looper.prepare()這個方法呢?原因在于ActivityThread中就已經調用了Looper.prepareMainLooper()這個方法,進而調用了Looper.prepare()方法。具體可以閱讀ActivityThread中的main函數源碼可知。
那麼,我們繼續探索第二種情況,也是我們常見的情況:
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
Bundle b = new Bundle();
b.putString("test", "test");
message.setData(b);
handler.sendMessage(message); //發送message
}
}).start();
通過上述方式發送資料是再常見不過了。可是我們想想,既然發送了資料,那麼資料被發送到哪裡去了?那麼,我們要再深入地看看sendMessage()方法是怎麼實作的:
//最終會調用sendMessageAtTime這個方法
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;
}
//隊列不為空,調用enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);
}
從上面的代碼可以看到,最後調用了enqueueMessage方法,即消息入隊的意思,表明我們發送的消息是存儲在消息隊列MessageQueue 的對象中。至于上述代碼中的mQueue,則在建立Looper對象時,一并建立了:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //建立消息隊列,一個Looper對應一個消息隊列
mThread = Thread.currentThread();
}
緊接着,我們看一下enqueueMessage方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //代表目前Handler對象
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); //調用隊列中的enqueueMessage方法
}
那麼,再看看MessageQueue中的enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
//代碼較長,使用簡單的僞代碼表示
synchronized (this) {
Message p = mMessages; //設定目前消息
//入隊操作
msg.next = p; // invariant: p == prev.next
prev.next = msg; //指向下一消息
}
return true;
}
到這裡,我們已經明白了發送的消息是如何處理的,那麼既然入隊了,肯定要知道它是如何處理隊列裡的消息。我們可以找到Looper中的loop()方法來解釋:
//同樣隻是摘取重要的代碼
public static void loop() {
final Looper me = myLooper(); //擷取Handler對應的Looper對象
final MessageQueue queue = me.mQueue; //擷取目前消息隊列
for (;;) { //不斷循壞的處理消息
Message msg = queue.next(); // 擷取消息
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg); //msg.target為Handler對象,回調dispatchMessage方法
msg.recycleUnchecked();
}
}
那麼Handler的最終實作方式應該就在dispatchMessage方法裡了:
public void dispatchMessage(Message msg) {
if (msg.callback != null) { //如果設定了回調函數,則調用設定的回調函數
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //如果沒有設定回調函數,則預設調用handleMessage方法
}
}
設定了回調函數的情況為:
final Handler handler = new Handler(new Handler.Callback() { //設定回調函數
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
關于設定回調函數進而調用handleMessage方法和不設定來調用的差別,個人了解在于優先級,也類似于線程中實作Runable接口和繼承Thread類。
好了,到這裡,關于Handler,MessegeQueue和Looper三者之間的關系基本理清了,我們可以用這種标準的形式來實作并闡述Handler機制:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Looper.loop();
}
}).start();
最後畫個示意圖來總結一下: