Android Handler消息機制源碼解析
好記性不如爛筆頭,今天來分析一下Handler的源碼實現
Handler機制是Android系統的基礎,是多線程之間切換的基礎。下面我們分析一下Handler的源碼實現。
Handler消息機制有4個類合作完成,分別是Handler,MessageQueue,Looper,Message
Handler : 獲取消息,發送消息,以及處理消息的類
MessageQueue:消息隊列,先進先出
Looper : 消息的循環和分發
Message : 消息實體類,分發消息和處理消息的就是這個類
主要工作原理就是:
Looper 類裏面有一個無限循環,不停的從MessageQueue隊列中取出消息,然後把消息分發給Handler進行處理
先看看在子線程中發消息,去在主線程中更新,我們就在主線程中打印一句話。
第一步:
在MainActivity中有一個屬性uiHandler,如下:
Handler uiHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); if(msg.what == 100){ Log.d("TAG","我是線程1 msg.what=" + msg.what + " msg.obj=" + msg.obj.toString()); }else if(msg.what == 200){ Log.d("TAG","我是線程2 msg.what=" + msg.what + " msg.obj=" + msg.obj.toString()); } } };
創建一個Handler實例,重寫了handleMessage方法。根據message中what的標識來區別不同線程發來的數據並打印
第二步:
在按鈕的點擊事件中開2個線程,分別在每個線程中使用 uiHandler獲取消息,並發送消息。如下
findViewById(R.id.tv_hello).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //線程1 new Thread(new Runnable() { @Override public void run() { //1 獲取消息 Message message = uiHandler.obtainMessage(); message.what = 100; message.obj = "hello,world"; //2 分發消息 uiHandler.sendMessage(message); } }).start(); //線程2 new Thread(new Runnable() { @Override public void run() { //1 獲取消息 Message message = uiHandler.obtainMessage(); message.what = 200; message.obj = "hello,android"; //2 分發消息 uiHandler.sendMessage(message); } }).start(); } });
使用很簡單,兩步就完成了從子線程把數據發送到主線程並在主線程中處理
我們來先分析Handler的源碼
Handler 的源碼分析
Handler的構造函數
public Handler() {
this(null, false);
}
調用了第兩個參數的構造函數,如下
public Handler(Callback callback, boolean async) {
//FIND_POTENTIAL_LEAKS 為 false, 不走這塊
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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
主要是下面幾句:
mLooper = Looper.myLooper(); 調用Looper的靜態方法獲取一個Looper
如果 mLooper == null ,就會拋出異常
Can‘t create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()";
說明我們的線程中如果沒有一個looper的話,直接 new Handler() 是會拋出這個異常的。必須首先調用 Looper.prepare(),這個等下講Looper的源碼時就會清楚了。
接下來,把 mLooper中的 mQueue賦值給Handler中的 mQueue,callback是傳出來的值,為null
這樣我們的Handler裏面就保存了一個Looper變量,一個MessageQueue消息隊列.
接下來就是 Message message = uiHandler.obtainMessage();
obtainMessage()的源碼如下:
public final Message obtainMessage()
{
//註意傳的是一個 this, 其實就是 Handler本身
return Message.obtain(this);
}
又調用了Message.obtain(this);方法,源碼如下:
public static Message obtain(Handler h) {
//1 調用obtain()獲取一個Message實例m
Message m = obtain();
//2 關鍵的這句,把 h 賦值給了消息的 target,這個target肯定也是Handler了
m.target = h;
//3 返回 m
return m;
}
這樣,獲取的消息裏面就保存了 Handler 的實例。
我們隨便看一下 obtain() 方法是如何獲取消息的。如下
public static Message obtain() {
//sPoolSync同步對象用的
synchronized (sPoolSync) {
//sPool是Message類型,靜態變量
if (sPool != null) {
//就是個單鏈表,把表頭返回,sPool再指向下一個
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
//如果sPool為空,則直接 new 一個
return new Message();
}
obtain()獲取消息就是個享元設計模式,享元設計模式用大白話說就是:
池中有,就從池中返回一個,如果沒有,則新創建一個,放入池中,並返回。
使用這種模式可以節省過多的創建對象。復用空閑的對象,節省內存。
最後一句發送消息uiHandler.sendMessage(message);
源碼如下:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
sendMessageDelayed(msg, 0) 源碼如下
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
又調用了sendMessageAtTime() 源碼如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// Handler中的mQueue,就是前面從Looper.get
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);
}
調用enqueueMessage()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//註意這句,如果我們發送的消息不是 uiHandler.obtainMessage()獲取的,而是直接 new Message()的,這個時候target為null
//在這裏,又把this 給重新賦值給了target了,保證不管怎麽獲取的Message,裏面的target一定是發送消息的Handler實例
msg.target = this;
// mAsynchronous默認為false,不會走這個
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最後調用queue.enqueueMessage(msg, uptimeMillis)
源碼如下:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don‘t have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
enqueue單詞的英文意思就是 排隊,入隊的意思。所以enqueueMessage()就是把消息進入插入單鏈表中,上面的源碼可以看出,主要是按照時間的順序把msg插入到由單鏈表中的第一個位置中,接下來我們就需要從消息隊列中取出msg並分了處理了。這時候就調用Looper.loop()方法了。
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;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
可以看到,loop()方法就是在無限循環中不停的從queue中拿出下一個消息
然後調用 msg.target.dispatchMessage(msg) , 上文我們分析過,Message的target保存的就是發送的Handler實例,這我們的這個demo中,就是uiHandler對象。
說白了就是不停的從消息隊列中拿出一個消息,然後發分給Handler的dispatchMessage()方法處理。
Handler的dispatchMessage()方法源碼如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到,一個消息分發給dispatchMessage()之後
1 首先看看消息的callback是否為null,如果不為null,就交給消息的handleCallback()方法處理,如果為null
2 再看看Handler自己的mCallback是否為null,如果不為null,就交給mCallback.handleMessage(msg)進行處理,並且如果返回true,消息就不往下分發了,如果返回false
3 就交給Handler的handleMessage()方法進行處理。
有三層攔截,註意,有好多插件化在攔截替換activity的時候,就是通過反射,把自己實例的Handler實例賦值通過hook賦值給了ActivityThread相關的變量中,並且mCallback不為空,返回了false,這樣不影響系統正常的流程,也能達到攔截的目的。說多了。
前面分析了handler處理消息的機制,也提到了Looper類的作用,下面我們看看Looper的源碼分析
Looper源碼分析
我們知道,APP進程的也就是我們應用的入口是ActivityThread.main()函數。
對這塊不熟悉的需要自己私下補課了。
ActivityThread.main()的源碼同樣經過簡化,如下:
文件位於 /frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
//1 創建一個looper
Looper.prepareMainLooper();
//2 創建一個ActivityThread實例並調用attach()方法
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
//3 消息循環
Looper.loop();
}
可以看到,主線程中第一句就是創建一個looper,並調用了Looper.loop()進行消息循環,因為線程只有有了一個looper,才能消息循環,才能不停的從消息隊列中取出消息,分發消息,並處理消息。沒有消息的時候就阻塞在那,等待消息的到來並處理,這樣的我們的app就是通過這種消息驅動的方式運行起來了。
我們來看下 Looper.prepareMainLooper()
的源碼,如下
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)
源碼如下:
private static void prepare(boolean quitAllowed) {
//1 查看當前線程中是否有looper存在,有就拋個異常
//這表明,一個線程只能有一個looper存在
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//2 創建一個Looper並存放在sThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
sThreadLocal的定義如下
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
是一個靜態的變量。整個APP進程中只有一個sThreadLocal,sThreadLocal是線程獨有的,每個線程都調用sThreadLocal保存,關於sThreadLocal的原理,其實就是類似HashMap(當然和HashMap是有區別的),也是key,value保存數據,只不過key就是sThreadLocal本身 ,但是映射的數組卻是每個線程中獨有的,這樣就保證了sThreadLocal保存的數據每個線程獨有一份,關於ThreadLocal的源碼分析,後面幾章會講。
既然Looper和線程有關,那麽我們來看下Looper類的定義,源碼如下:
/**
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
*/
public final class Looper {
.......
}
我們看上面的註釋
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
這就是經典的Looper的用法 ,可以在一個線程中開始處調用 Looper.prepare();
然後在最後調用 Looper.loop();進行消息循環,可以把其它線程中的Handler實傳進來,這樣,一個Looper線程就有了,可以很方便的切換線程了。
下章節我們來自己設計一個Looper線程,做一些後臺任務。
Handler的消息機制源碼就分析到這了
Android Handler消息機制源碼解析