對於Handler搞android的都熟悉,大概原理也知道,可能很多開發者也看過源碼,本人也看過源碼,但是一直沒有系統的分析過,總結過,今天來一波對Handler的源碼分析,本文需要讀者瞭解handler的基本原理,如果不瞭解請參考Handler消息傳遞機制!
廢話不在多說,直接開整!!!
看官:博主,從哪裏開始分析呢?
博主:嗯,咱們就從Message message = Message.obtain();
開始入手
然我們進入Message找到obtain方法:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
看官:博主,英文註釋是啥意思?
博主:帶我給各位翻譯,意思:從全局消息池中返回一個實例,能使我們在很多情況下創建新對象(聯想一下線程池)
在上面的代碼中主要就是返回一個Message對象,但是這個對象並不是直接的創建出來的,而是先從Message Pool中去取,如果取不到,然後再創建,我們來分析一下主要代碼:
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
首先要明白,Message Pool其實是一個鏈表,其中每一個節點就是一個Message,sPool代表的就是第一個節點,在sPool不爲空的時候,取出第一個節點賦值給m,注意接下來的動作就是把下一個節點弄成第一個節點,m.next
就代表下一個節點,然後spool指向它,此時,第一個節點的指針還是m,第二個節點的指針是sPool,然後讓m.next
爲null,目的是爲了把第一個節點從鏈表中移除,讓下一個節點成爲真正的第一個節點,接着sPoolSize--
代表此鏈表中的節點的個數少了一個,如果sPool
減爲0個,那次方法就會執行return new Message()
,即創建一個Message對象。
好了,現在我們有消息了,然後我們就需要關注怎麼把這個消息發送出去的代碼了,發送消息的代碼是在Handler對象中的,所以我們先來看看Handler.
通常我們實例化的都是無參數的Handler,我們來看此構造方法:
public Handler() {
this(null, false);
}
看官:我靠,這麼簡單!
博主:這叫代碼少好不,那能叫簡單,你能一眼看出這是啥意思?
我們接着進入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();
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;
}
看官:我靠,有沒有搞錯,折磨長,確實不簡單。
博主:別怕,我們只分析主要的代碼
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;
首先通過Looper.myLooper()
得到一個輪詢器,如果輪詢器(Looper)爲null,就會拋出一個異常"Can't create handler inside thread that has not called Looper.prepare()"
,異常的意思是說,因爲沒有調用Looper.prepare()
所以不能再線程中創建handler,得到Looper之後,接着利用它得到消息隊列。
我們知道了Looper是怎麼取出來的,那我們是怎麼存進去的呢?
我們先計入到myLooper()
中:
/**
* 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();
}
我們看到是通過sThreadLocal
得到的,接着我們找一找是不是有set()方法。
還真有:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
我們看看set方法在哪裏被調用:
在Looper中我們找到了一段這樣的代碼:
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));
}
看到這,還記不記得我們前面說過的,如果取Looper的時候不進行prepare就會拋出異常,原來這個方法的作用就是設置Looper,另外還有一點要注意,設置和取出Looper的時候,使用的是ThreadLocal,ThreadLocal
的作用是在線程內是單例的,就是在同一個線程中,我們設置的Looper和取出的Looper是同一個。
這裏順便說一下,我們的消息隊列是在哪裏創建的呢?進入Looper中:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以說,有了Looper,纔有消息隊列。
讀者可能有疑問,說我在主線程中使用handler的時候,沒有調用Looper啊,怎麼不拋出異常呢?
這就需要看一看ActivityThread中的main()方法了:
public static void main(String[] args) {
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop(); //進行消息的輪詢,爲什麼我們的主程序在不點擊的時候也不會退出?就是因爲有loop方法,內部是一個死循環。
throw new RuntimeException("Main thread loop unexpectedly exited");
}
發現有這樣一行代碼:Looper.prepareMainLooper();
我們進去看看:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
哈哈,調用了prepare(false)
,原來主線程自動爲我們調用了prepare,但是當我們在子線程中創建handler的時候,不要忘記調用prepare,子線程可不會爲我們自動調用。
ok,接下來我們就可以研究最後一步了,就是怎麼發消息的代碼!
看官:發送消息的方法有很多啊,我們從哪一個說起呢?
博主:這位看官說的對,發送消息的方法確實有很多,但是,其實他們的內部用的是同一個方法發送的消息
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
是不是發現了其實方法的內部都是通過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;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
我們看到先得到一個消息隊列(MessageQueue),如果不能得到這個消息隊列,也就是queue==null,就會拋出異常,否則就會返回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;
,意思是把當前的類保存到msg中,當前類?對,當前的類就是一個handler,這句代碼的含義就是把handler和msg聯繫起來,當處理的時候就用這個handler進行處理。
返回的結果就是把消息插入到消息隊列中,具體的插入方法和鏈表的插入是一樣的,爲什麼和鏈表的插入方式一樣?我們前面不是說過了嗎,消息隊列其實是一個鏈表。
由此看出,其實發送消息就做了一件事,就是把消息插入到消息隊列。
現在消息隊列中已經有消息了,我們現在既可以取出消息進行處理了,怎麼取出消息呢,用Looper.loop()方法:
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.recycleUnchecked();
}
}
看官:我靠,折磨複雜?
博主:仔細看看好不好,一看長就說複雜,哎
分析以上代碼,它先執行Looper me = myLooper();
得到Looper,然後又通過MessageQueue queue = me.mQueue;
得到消息隊列,然後就是一個死循環
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
用來獲得消息,接下來又有一句這樣的代碼msg.target.dispatchMessage(msg);
,想要知道他是幹什麼的,只有進去看看啦:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
我們看到有這樣的代碼:
if (msg.callback != null) {
handleCallback(msg);
}
我們很好奇,這個callback是什麼玩意,這個其實就是一個Runnable,還記得我們使用handler的時候,handler有一個post方法:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
此方法傳入一個Runnable參數,然後我們進入getPostMessage(r)
中:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
這就不解釋了,相信大家都能看懂。
回到dispatchMessage方法,我們分析一下他,首先如果我們向消息隊列中插入一個Runable就會執行handleCallback(msg);
方法,其實就是一個run方法,否則就會判斷mCallback != null
是否爲空,mCallback是什麼呢?這其實是一個接口,我們知道handler還有一種使用方法,就是
Handler.Callback callback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
};
Handler handler = new Handler(callback){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
這種處理消息的方式,按照dispatchMessage方法的邏輯,上面的那個方法會處理消息?看else中的代碼:
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
如果有handler.callback的時候,會優先調用mCallback中的處理消息方法。
最後對dispatchMessage方法做一個總結,就是當傳入的是Runnable的時候,先處理Runnable,否則判斷是不是有Callback,如果有就先處理它,如果沒有或者沒有處理,那就在自己覆寫的handleMessage方法中處理消息。
OK,Handler基本原理大概就分析完了,多謝大家收看!
如有錯誤,敬請指出,不勝感激!