為什么要使用Handler
Handler是Android提供的一套消息機制搓劫。由于Android的開發(fā)規(guī)范限制(UI控件非線程安全),更新UI的操作必須在主線程完成琢感。所以大部分人對Handler的理解主要是用來更新UI的碗暗,包括我一直以來也都是這樣理解滴。但這僅僅是Handler的一個特殊使用場景昌阿。
和Handler一起為大家所知的還有它的兩兄弟Looper和MessageQueue饥脑,這三駕馬車一起構成了Android的消息機制,但是本文只討論Handler懦冰。
Handler是如何實現消息機制的
Handler重載了很多的構造方法灶轰,但是內部原理都一樣。在創(chuàng)建時會使用當前線程的Looper來構建內部的消息循環(huán)系統(tǒng)刷钢。
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) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();// 引用當前線程的Looper
if (mLooper == null) {// mLooper什么時候會為空笋颤?只有當在子線程中創(chuàng)建Handler,又未提前調用Looper.prepare()
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;// 引用Looper中的消息隊列
mCallback = callback;// 這個是Handler.Callback對象内地,等會我們會詳細講解
mAsynchronous = async;
}
Handler創(chuàng)建完畢后伴澄,就可以通過這個handler對象發(fā)送一個消息赋除,這個消息會進入消息隊列,因為Looper的消息隊列一直在循環(huán)秉版,一旦消息到來贤重,就會通過Handler的dispatchMessage()方法來進行分發(fā)處理茬祷。因為Looper是運行在創(chuàng)建Handler所在的線程清焕,為什么這么說,看如下源碼:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {// prepare在一個線程中只能調用一次
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));// 實例化的Looper被放入線程變量祭犯,接下來我們看看Looper的構造器做了什么工作
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);// 初始化了消息隊列
mThread = Thread.currentThread();// 關聯(lián)了當前的線程
}
所以這樣一來Handler中的業(yè)務邏輯就被切換到創(chuàng)建Handler所在的線程中去執(zhí)行了秸妥。
依據上述,糾正很多初學者的一點疑慮:Handler也可以在子線程中創(chuàng)建沃粗,只要姿勢正確粥惧,先調用一下Looper.prepare()就可以。這樣創(chuàng)建的子線程也能擁有消息機制最盅,但這個Handler是不能做UI更新的突雪。
Handler使用過程中問題
通常大家看到很多Handler實例化的過程是這樣的:
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
// 根據消息類別做處理
}
};
這時候會看到編輯器友好善意的Warning: * This Handler class should be static or leaks might occur (anonymous android.os.Handler)*
這個問題是說Handler應該被聲明稱靜態(tài)內部類,否則就可能會導致內存泄露涡贱。what the fu*k r u saying? 因為java中匿名類默認持有外部類對象的引用咏删,不然你也不可能直接在內部類里面直接使用外部類的屬性。如果外部類正欲銷毀问词,而在handleMessage里面恰好有新的消息到達需要處理督函,匿名類持有外部類對象就不會被釋放。另外激挪,注意非靜態(tài)內部類也默認持有外部類對象的引用辰狡。
解決方案
方案一:
只要將匿名類修改成靜態(tài)的內部類,并將外部類改為弱引用垄分,例如:
private static final class MyHandler extends Handler{
private WeakReference<? extends Activity> mReference;
public MyHandler(Activity activity){
mReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
// 消息處理
Activity activity = mReference.get();
switch(msg.what){
case XX:
if(activity != null){
// 做你的UI更新去吧
}
break;
}
}
}
并在外部類銷毀的時候調用Handler的removeCallbacksAndMessages(null)去釋放當前handler發(fā)送到消息隊列的消息宛篇。咦,這里為什么還有CallBack呢薄湿?因為handler還可以post一個Runnable對象些己,而這個對象會被包裝成Message對象,這個是在消息分發(fā)的時候優(yōu)先執(zhí)行的嘿般。不信段标,你看
public void dispatchMessage(Message msg) {
if (msg.callback != null) {// post出去的Runnable
handleCallback(msg);
} else {
if (mCallback != null) {// 這里我們可以實現Handler.Callback接口來處理消息
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);// 通常重新的handleMessage消息處理方法
}
}
方案二:
如果認真看以上Handler的消息分發(fā)dispatchMessage()的執(zhí)行流程,不難發(fā)現炉奴,我們還可以這樣安全的使用Handler逼庞。
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// 根據消息類別做處理
return false;
}
});
以上觀點純屬個人見解,若有出入瞻赶,歡迎各位書友一起探討赛糟。