Handler本身可在多線程之間調(diào)用辟灰,不管它在哪個線程發(fā)送消息端礼,都會回到它被初始化的哪個線程中接收到消息掰茶。
初始化
Handler有7個構(gòu)造方法,分別對應(yīng)不同的參數(shù)來初始化不同的Handler屬性目代,但是真正完成初始化操作的只有兩個構(gòu)造方法:
// 是否需要查找潛在的漏洞
private static final boolean FIND_POTENTIAL_LEAKS = false;
/**
* 將Callback接口作為構(gòu)造方法參數(shù),可以用作接收消息的回調(diào)
* 這樣就可以省去自己重寫Handler自身的handleMessage方法
*
* @param msg 接收到的消息
* @return 是否需要進一步處理,即調(diào)用Handler自身的handleMessage方法
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
// 發(fā)送的消息是否為異步的榛了,默認是false
final boolean mAsynchronous;
/**
* @hide 隱藏的構(gòu)造方法在讶,外部不可見
*/
public Handler(Callback callback, boolean async) {
// 檢查是否存在內(nèi)存泄漏的可能
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
// 得到當(dāng)前線程的Looper
mLooper = Looper.myLooper();
// 如果Looper還沒初始化拋出異常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 得到當(dāng)前線程的MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
從上面代碼的得知,構(gòu)造方法初始化的工作就是給mLooper
,mQueue
,mCallback
和mAsynchronous
這幾個關(guān)鍵的屬性賦值.mLooper
和mQueue
自然就是當(dāng)前線程下的Looper和MessageQueue了霜大,如果傳遞了Looper參數(shù)就直接賦值构哺,如果沒傳遞就調(diào)用Looper.myLooper();
得到當(dāng)前線程的Looper。
mCallback
是Handler內(nèi)部定義的一個簡單接口战坤,其目的是為了替代傳統(tǒng)的接收消息方法曙强。當(dāng)然使用mCallback
的同時并不會影響正常的Handler消息分發(fā)。此處解釋從后面接收消息時的邏輯就可以看到途茫。
mAsynchronous
的意思是該Handler發(fā)送的消息是否是異步的碟嘴,從前面Message源碼的文章中我們知道Message中有一個設(shè)置消息是否為異步消息的方法,MessageQueue對異步消息的處理也與同步消息不同囊卜。此處如果設(shè)置了mAsynchronous
為true
娜扇,那么這個Handler發(fā)送的所有消息就都是異步消息。
在有兩個參數(shù)的構(gòu)造方法中我們會發(fā)現(xiàn)有一段檢查是否存在內(nèi)存泄漏的代碼栅组,為什么會這樣呢雀瓢?在分析完發(fā)送消息和接收消息后再說這個。
當(dāng)然玉掸,除了上面兩個構(gòu)造方法外還有其它幾個構(gòu)造方法致燥,但均是調(diào)用上面兩個方法:
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
/**
* @hide 隱藏的構(gòu)造方法,外部不可見
*/
public Handler(boolean async) {
this(null, async);
}
發(fā)送消息
使用Handler發(fā)送消息時我們知道它分為兩類:
-
postXXX()
方法切換回原線程排截。 -
sendMessageXXX()
方法發(fā)送消息到原線程嫌蚤。
其實這兩種方法本質(zhì)都是發(fā)送一個Message對象到原線程,只不過PostXXX()
方法是發(fā)送了一個只有Runnable callback
屬性的Message對象断傲。
先來看一下sendMessageXXX()
類的方法:
// 發(fā)送一條普通消息
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
// 發(fā)送一條空消息
public final boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what, 0);
}
// 發(fā)送一條空的延時消息
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
// 發(fā)送一條空的定時消息
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
// 發(fā)送一個普通的延時消息
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
// 發(fā)送一個普通的定時消息
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//得到消息隊列
MessageQueue queue = mQueue;
// 如果消息隊列是空的記錄日志然后結(jié)束方法
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
// 執(zhí)行消息入隊操作
return enqueueMessage(queue, msg, uptimeMillis);
}
從上面的代碼中發(fā)現(xiàn)脱吱,不管調(diào)用何種發(fā)送消息的方法,最后真正調(diào)用的都是sendMessageAtTime()
方法认罩。而真正發(fā)送的核心方法也就是入隊方法是Handler的enqueueMessage()
方法箱蝠。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 將消息的宿主設(shè)置為當(dāng)前Handler自身
msg.target = this;
//如果Handler被設(shè)置成了異步就把消息也設(shè)置成異步的
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 執(zhí)行消息隊列的入隊操作
return queue.enqueueMessage(msg, uptimeMillis);
}
看到這里終于明白了為啥Looper和MessageQueue一直在使用Message的target
屬性而我們卻從來沒有給它賦值過,是Handler在發(fā)送消息前自己賦值上去的垦垂。
看完了發(fā)送消息類的方法在看看切換線程類的方法干了什么:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis){
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean postAtFrontOfQueue(Runnable r){
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
上本節(jié)開頭講的一樣宦搬,postXXX()
類方法就是構(gòu)造了一個只有Runnable callback
的Message對象,然后走正常發(fā)送消息的方法劫拗。唯一有一個特例就是postAtFrontOfQueue()
方法间校,它調(diào)用了sendMessageAtFrontOfQueue()
方法是之前發(fā)送消息沒有用到過的:
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
原來這個方法特殊的地方就是在入隊的時候時間參數(shù)為0,我們在MessageQueue源碼知道如果入隊消息的時間參數(shù)為0那么這個消息會被直接放在隊列頭页慷。所以憔足,postAtFrontOfQueue()
方法就是直接在消息隊列頭部插入了一個消息胁附。
接收消息
在Looper 的源碼中我們知道每當(dāng)從MessageQueue中取出一個消息時就會調(diào)用這個消息的宿主target
中分發(fā)消息的方法:
// Looper分發(fā)消息
msg.target.dispatchMessage(msg);
而這個宿主target
也就是我們的Handler,所有Handler接收消息就是在這個dispatchMessage()
方法中了:
public void dispatchMessage(Message msg) {
// 如果Message的callback不為空滓彰,說明它是一個通過postXXX()方法發(fā)送的消息
if (msg.callback != null) {
// 直接運行這個callback
handleCallback(msg);
} else {
//如果mCallback 不為空說明Handler設(shè)置了Callback接口
// 先執(zhí)行接口處理消息的方法
if (mCallback != null) {
// 如果callback接口處理完消息返回true說明它將消息攔截
// 不再執(zhí)行Handler自身的處理消息方法控妻,直接結(jié)束方法
if (mCallback.handleMessage(msg)) {
return;
}
}
// 調(diào)用Handler自身處理消息的方法
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
// 運行callback,也就是這個Runnable接口
message.callback.run();
}
// Handler自身處理消息的方法揭绑,開發(fā)者需要重新該方法來實現(xiàn)接收消息
public void handleMessage(Message msg) {
}
由此可見弓候,如果是單純的PostXXX()
方法發(fā)送的消息,Handler接收到了之后直接運行Message對象的Runnable接口他匪,不會將它當(dāng)做一個消息進行處理菇存。
而我們的mCallback
接口是完全可以替代Handler自身接收消息的方法,因為其高優(yōu)先處理等級诚纸,它甚至可以選擇攔截掉Handler自身的接收消息方法撰筷。
內(nèi)存泄漏的可能
我們在使用Handler的時候?qū)懛ㄒ话闳缦拢?/p>
private final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
}
};
由于重寫了handleMessage()
方法相當(dāng)于生成了一個匿名內(nèi)部類,也就相當(dāng)于如下代碼:
private final Handler handler = new MyHandler ();
class MyHandler extends Handler{
@Override
public void handleMessage(Message msg) {
}
}
可是你有沒有想過內(nèi)部類憑什么能夠調(diào)用外部類的屬性和方法呢畦徘?答案就是內(nèi)部類隱式的持有著外部類的引用毕籽,編譯器在創(chuàng)建內(nèi)部類時把外部類的引用傳入了其中,只不過是你看不到而已井辆。
既然Handler作為內(nèi)部類持有著外部類(多數(shù)情況為Activity)的引用关筒,而Handler對應(yīng)的一般都是耗時操作。當(dāng)我們在子線程執(zhí)行一項耗時操作時杯缺,用戶退出程序蒸播,Activity需要被銷毀,而Handler還在持有Activity的引用導(dǎo)致無法回收萍肆,就會引發(fā)內(nèi)存泄漏袍榆。
解決方法分為兩步
- 生成內(nèi)部類時把內(nèi)部類聲明為靜態(tài)的。
- 使用弱引用來持有外部類引用塘揣。
靜態(tài)內(nèi)部類不會持有外部類的引用包雀,且弱引用不會阻止JVM回收對象。
static class MyHandler extends Handler{
WeakReference<Activity> mActivity ;
public MyHandler(Activity activity){
mActivity = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
Activity activity = mActivity.get();
if (activity == null){
return;
}
// do something
}
}
所以亲铡,在文章剛開始初始化方法中檢查漏洞的代碼其實就是檢查這個內(nèi)存泄漏的可能性:
// 檢查是否存在內(nèi)存泄漏的可能
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
// 是否為匿名類才写,內(nèi)部類以及是否為靜態(tài)類
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
大功告成,完美奖蔓!