1.Handler存在的意義
對(duì)于我們而言主要是用于刷新UI饥努,因?yàn)樽泳€程不能刷新UI,如果你對(duì)handler有充分的了解,還是可以用到其他地方的绒北,鴻洋大神有篇博客是用handler加載圖片的吃引,不妨去研究一番。
2.掌握Handler一些必備知識(shí)點(diǎn)
要想實(shí)現(xiàn)子線程通知Handler刷新UI是比較簡單的妇汗,你只要看下別人的寫法自己就能模仿寫出來,寫多幾次就能無腦的寫出來了说莫,我以前就是這么的無腦操作使用Handler刷新UI的杨箭。但是,知恥而后勇的我良心發(fā)現(xiàn)储狭,這么無腦不行啊互婿,決定去看下各位大神對(duì)Handler的講解,總算有一番了解辽狈。
不扯淡慈参,Handler、Looper刮萌、MessageQueue三個(gè)是一起工作的驮配,以我的表達(dá)能力根本不用畫圖就能講清他們之間的關(guān)系,先從三個(gè)單詞了解它們着茸,Handler是處理者的意思壮锻,MessageQueue是消息隊(duì)列的意思,Looper循環(huán)的意思涮阔,他們之間的關(guān)系很簡單猜绣,Handler有幾個(gè)方法可以發(fā)送信息(當(dāng)我們的子線程需要通知刷新UI的時(shí)候就這么做)到MessageQueue,Looper的loop()方法一直循環(huán)讀取MessageQueue里面的消息敬特,然后將消息分發(fā)給對(duì)應(yīng)的Handler掰邢,一般是把信息交給Handler的HandlerMessage()方法,所以我們?cè)贖andlerMessage()處理消息(比如拿到的數(shù)據(jù)顯示在對(duì)應(yīng)的view上面)伟阔,這是一個(gè)大概的流程辣之,代碼展示大概是如下幾步:
3.從源碼才能更加了解更多的細(xì)節(jié)(涉及到Handler、Looper皱炉、MessageQueue怀估、Message的源碼)
-
1.在UI線程中創(chuàng)建兩個(gè)Handler(我們這里講的Handler、Looper娃承、MessageQueue都是屬于UI線程的)
private Handler mHandler1 = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); LLog.d("handleMessage", "" + msg.what); } } ; private Handler mHandler2 = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); LLog.d("handleMessage", "" + msg.what); } };
-
2.在子線程中使用mHandler1和mHandler2發(fā)送消息到同一個(gè)MessageQueue(主線程始終只有一個(gè))
new Thread() { @Override public void run() { super.run(); //在子線程中使用UI線程的Handler將想要發(fā)送的信息發(fā)送到MessageQueue中 //Looper會(huì)一直從MessageQueue拿到信息交給handleMessage()奏夫,所以我們只需要在handleMessage() //對(duì)消息進(jìn)行處理即可怕篷,其實(shí)主線程只有一個(gè)MessageQueue mHandler1.sendEmptyMessage(1); mHandler2.sendEmptyMessage(2); } }.start();
為什么sendEmptyMessage()之后历筝,消息就來到了handleMessage(Message msg)呢酗昼?
因?yàn)閚ewHandler()和sendEmptyMessage()方法隱蔽了很多的細(xì)節(jié),所以我們需要打開Handler的源碼才能知道流程梳猪,請(qǐng)看源碼:
public 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) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper(); //拿到當(dāng)前線程(UI線程)的Looper()實(shí)例
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
是的麻削,看注釋我們發(fā)現(xiàn)在這里我們拿到了一個(gè)需要的Looper實(shí)例,前面說過需要Looper的loop()方法不斷的從MessageQueue中不斷獲取Message(消息)春弥,所以必須要有一個(gè)Looper實(shí)例呛哟,不急著看Handler的其他源碼,先點(diǎn)進(jìn)這里L(fēng)ooper.myLooper()看下是如何獲取到Looper實(shí)例的匿沛。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
Looper源碼這里是拿到了當(dāng)前線程的Looper實(shí)例扫责,為什么一get就能有呢?因?yàn)橹骶€程創(chuàng)建的時(shí)候已經(jīng)幫我們set進(jìn)去了逃呼,所以我們不用收到set進(jìn)去鳖孤,到底是如何set進(jìn)去的?這是后就需要靈性了在當(dāng)前類搜索找到sThreadLocal.set,是的抡笼,找到了:
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
最后一行看到了吧苏揣,簡單粗暴直接new了一個(gè)Looper()進(jìn)去,外層方法是prepare()推姻,方法名是準(zhǔn)備的意思平匈,看來就是調(diào)用這個(gè)準(zhǔn)備方法的時(shí)候就創(chuàng)建了一個(gè)Looper實(shí)例set進(jìn)去了,Looper的構(gòu)造函數(shù)里面直接創(chuàng)建了一個(gè)MessageQueue藏古,MessageQueue(消息隊(duì)列)是用于存放Message(消息)的增炭,此時(shí)三個(gè)基佬都有了各自的實(shí)例了,那么就可以合作干活了拧晕。
如果你對(duì)sThreadLocal感興趣弟跑,其實(shí)他就是一個(gè)static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();不懂自行學(xué)習(xí),我也不太懂先
說了這么多連Looper在哪里創(chuàng)建的都不知道防症,我TM差點(diǎn)都忘了孟辑。對(duì)于這個(gè)UI線程的Looper實(shí)例是不用我們進(jìn)行創(chuàng)建的,很多關(guān)于這部分知識(shí)的講解都有講到蔫敲,Android 在進(jìn)程的入口函數(shù) ActivityThread.main()中饲嗽,調(diào)用 Looper.prepareMainLooper, 為應(yīng)用的主線程創(chuàng)建Looper,源碼:
public final class ActivityThread {
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
if (activity != null) {
activity.attach(appContext, this, ..., );
public static void main(String[] args) {
// 在這兒調(diào)用 Looper.prepareMainLooper, 為應(yīng)用的主線程創(chuàng)建Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
}
看到注釋那里奈嘿,Looper.prepareMainLooper()這里創(chuàng)建了主線程Looper貌虾,而不是調(diào)用Looper.prepare()方法創(chuàng)建,其實(shí)Looper.prepareMainLooper()調(diào)用了Looper.prepare(false),最終還是調(diào)用了裙犹,看到這里的最后一行
Looper.loop()這個(gè)方法點(diǎn)擊進(jìn)去看是一直獲取MessageQueue的Message的尽狠,看源碼見分曉(略長衔憨,跟著注釋看):
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper(); //這個(gè)方法前面講過,就是Looper 實(shí)例袄膏,如果你忘了這個(gè)方法很正常践图,看多幾次就OK
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //這個(gè)是消息隊(duì)列,等下就要掏空它(從里面不斷獲取Message)
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//這里開始進(jìn)入死循環(huán)沉馆,不斷獲取消息码党,分發(fā)消息
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//這句才是關(guān)鍵,下面會(huì)對(duì)這句局進(jìn)行分析
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
以上邏輯相當(dāng)?shù)暮唵纬夂冢灰裮sg.target.dispatchMessage(msg)這個(gè)函數(shù)流程走完就OK了揖盘,target是啥?為什么它有dispatchMessage(msg)方法锌奴,靈性又發(fā)揮作用了兽狭,msg.target.dispatchMessage(msg)這里面的msg是從消息隊(duì)列拿到的消息,我們知道消息最后是在Handler的方法里進(jìn)行處理的鹿蜀,那target應(yīng)該就是Handler實(shí)例了箕慧,馬上去Message源碼搜索target,是的耻姥,我們發(fā)現(xiàn)他就是Handler销钝,源碼:Handler target;,這時(shí)我們需要知道什么時(shí)候?qū)⑦@個(gè)target賦值的琐簇,回到最開始我們new 出一個(gè)Handler的那里蒸健,只有在那里我們才操作了Handler,我們調(diào)用了handler.sendEmptyMessage();,看這里的源碼婉商,一直跟蹤下去:
/**
* Sends a Message containing only the what value.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
/**
* Sends a Message containing only the what value, to be delivered
* after the specified amount of time elapses.
* @see #sendMessageDelayed(android.os.Message, long)
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
/**
* Sends a Message containing only the what value, to be delivered
* at a specific time.
* @see #sendMessageAtTime(android.os.Message, long)
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, 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) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
跟了大半天才跟蹤到這行msg.target = this; 是的,我們?cè)谶@里將Handler放到了Message的targe字段里面了似忧,發(fā)了一條消息過來肯定要做個(gè)標(biāo)志嘛,不然這么多消息扔到消息隊(duì)列丈秩,取出來都不知道給回誰盯捌,所以我們?cè)跅l消息對(duì)象標(biāo)記好各自的Handler,最后取出來才能給到對(duì)應(yīng)的Handler蘑秽。好的饺著,知道了targe是Handler,那么msg.target.dispatchMessage(msg);的dispatchMessage(msg)方法就是在Handler里面了肠牲,終于把消息給回老子(Handler)了.源碼源碼源碼幼衰,看下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
直接看else里面的,if里面的的情況需要另外分析缀雳,我們發(fā)送的這些消息會(huì)交給else處理就是了渡嚣,handleMessage(msg)等你等到想分手了,經(jīng)歷了這么多,讓我更見的了解你识椰,以后再也不會(huì)找這么久了绝葡。
記得前面是這么寫的
mHandler1.sendEmptyMessage(1);
mHandler2.sendEmptyMessage(2);
那么很自然的,下面一個(gè)輸出1腹鹉,一個(gè)輸出2
private Handler mHandler1 = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
LLog.d("handleMessage", "" + msg.what);
}
} ;
private Handler mHandler2 = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
LLog.d("handleMessage", "" + msg.what);
}
};
第一次敲博客藏畅,如果哪里講錯(cuò)或者思路有些逆天,麻煩指點(diǎn)下种蘸,如果你是剛接觸Handler墓赴,很有可能需要多看幾次才能理解竞膳。如果你完全理解了航瞭,就在子線程寫個(gè)Handler,在子線程發(fā)送信息坦辟,讓Handler處理信息吧刊侯。