本文是在閱讀張鴻陽(yáng)的深入理解 Looper、Handler、Message三者關(guān)系的一個(gè)自我學(xué)習(xí)總結(jié)
Looper
Looper主要作用:
- 綁定當(dāng)前線程,new一個(gè)MessageQueue并綁定 ,且保證一個(gè)線程只有一個(gè)Looper
- 不斷進(jìn)行消息輪詢,有則分發(fā),無(wú)則阻塞
Looper主要是prepare()和loop()兩個(gè)方法。
prepare()方法
** 作用:初始化Looper,保證一個(gè)線程初始化一次**
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true));
}
sThreadLocal是一個(gè)ThreadLocal對(duì)象,可以在一個(gè)線程中存儲(chǔ)變量。將一個(gè)Looper的實(shí)例放入了ThreadLocal,判斷sThreadLocal是否為null,否則拋出異常。** 說(shuō)明了Looper.prepare()方法不能被調(diào)用兩次,同時(shí)也保證了一個(gè)線程中只有一個(gè)Looper實(shí)例.**-
構(gòu)造方法Looper()
** 作用:綁定當(dāng)前線程,new一個(gè)MessageQueue并綁定**private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
new了一個(gè)MessageQueue(消息隊(duì)列)給全局變量mQueue,獲取當(dāng)前線程賦給全局變量mThread,在prepare()調(diào)用了構(gòu)建方法.
-
loop()方法
** 不斷輪詢并有消息分發(fā)消息 **public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // 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(); 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); } 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.recycle(); } } public static @Nullable Looper myLooper() { return sThreadLocal.get();}
a. 首先在sThreadLocal取一個(gè)Looper.
b. 然后不斷輪詢消息隊(duì)列,當(dāng)有消息取出的時(shí)候, msg.target.dispatchMessage(msg);msg.targer實(shí)際為Handle,那么這個(gè)** Handle怎樣與Message關(guān)聯(lián),Message怎么進(jìn)入消息隊(duì)列的呢? **
c. 釋放msg,msg.recycle();
Handle
-
構(gòu)造方法Handle()**
作用:獲取當(dāng)前線程中Looper,獲取 Looper的Queue賦給全局變量 **
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(); 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ā)送消息sendMesssage(Message msg)
** 作用:使用全局mQueue發(fā)送消息,并且將msg.targer設(shè)為this(Handle),解釋了Handle如何與MessageQueue,msg關(guān)聯(lián) **private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
-
接收處理消息
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
覆寫(xiě)Handle的handleMessage就能處理消息了.
Message,Messenger
Message作為傳遞中的數(shù)據(jù)載體.使用Message#obtian()進(jìn)行構(gòu)建,obj用Bundle傳遞序列化參數(shù).
Messenger相當(dāng)于Handle的封裝,用于多進(jìn)程間通信.
后記
面試上我有寫(xiě)這個(gè),面試的時(shí)候老喜歡讓我講講Handle,筆試碰到幾率也高。我一般三句話就完了:
1.Looper輪詢器,不斷輪詢,有消息取出,沒(méi)則阻塞。
2.每個(gè)線程只有一個(gè)輪詢器,消息隊(duì)列。
3.message是消息載體。
這答案我自己聽(tīng)了都想哭。
所以,我回來(lái)再看一般,然后整理一個(gè)答案:
1.初始化Handle實(shí)例的時(shí)候獲取當(dāng)前線程的Looper輪詢器。
2.將Looper中的消息隊(duì)列賦值給Handle中對(duì)象中全局變量。
3.Handle利用消息隊(duì)列發(fā)送消息,消息發(fā)的targer設(shè)置成自己。所以能夠跨線程通信。
4.Looper的parper方法保證每個(gè)線程只初始化一次,構(gòu)造方法綁定當(dāng)前線程,new出消息隊(duì)列。loop方法不斷輪詢消息,有讀取,無(wú)阻塞。
還有會(huì)讓你說(shuō)說(shuō)線程間通信,Handle是要講的,用在主線程與子線程通信,因?yàn)樽泳€程無(wú)法更新ui.
但是一般線程間的通信其實(shí)是共享同進(jìn)程內(nèi)存,讀取共享的內(nèi)存變量(大家都能訪問(wèn)到,比如全局變量,普通類(lèi)的static變量)就能實(shí)現(xiàn)簡(jiǎn)單的通信了。