Handler在android中主要用于接受子線程的消息,然後在主線程中更新UI。因為android規定,不能在子線程中更新UI。如果在子線程中進行UI操作就會報以下錯誤:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
但是,android又規定,在UI線程,也就是主線程中不能進行耗時操作,比如網絡請求,否則可能會産生ANR問題,是以引入了handler機制來對UI進行更新操作。
Handler常用的方法有兩種:
<span style="font-size:18px;">public class TestActivity extends AppCompatActivity {
private static Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
new Thread(new Runnable() {
@Override
public void run() {
// 方式一
handler.sendEmptyMessage(0x001);
}
}).start();
//方式二
new Handler().post(new Runnable() {
@Override
public void run() {
}
});
}
}</span>
方式一:就是在主線程中建立handler,通過handler.sendMessage的方法調用主線程中handler的handlerMessage方法處理,進而進行UI更新;
方式二:handler.post(runnable)。來進行一些延時操作。
但是這些方法的使用原理是什麼呢,我們來看一下源碼。
我們從第一步new handler()開始研究。
<span style="font-size:18px;"> public Handler() {
this(null, false);
}</span>
這是new Handler()的構造方法,就是調用了另外一個構造方法,屬性設為null,false。
再看其調用的自身的構造方法。
<span style="font-size:18px;"> 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;
}</span>
這裡就是對handler進行一些初始化操作,其中關鍵的代碼為:
<span style="font-size:18px;"> mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}</span>
這段話的意思就是,擷取目前線程的Looper對象,如果目前線程沒有建立Looper則會報錯:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
意思就是在調用new Handler()前必須調用Looper.prepare()方法,否則就會報錯。這裡就很奇怪了,我們平常使用過程中也沒有調用這個方法,但是程式也沒報錯啊。這是因為,在Android的入口類:ActivityThread中,main方法已經初始化調用了Looper.prepared()。也就是在主線程中,系統已經預設建立了Looper對象,是以,我們在主線程中new Handler()并不需要自己再去調用一遍。這也就是我們沒有去自己寫Looper.prepared()的原因。但是,當我們在子線程中去new Handler 的時候必須自己去調用Looper.prepared()否則就會出錯。
我們再來順着看一下Looper.prepared()方法:
<span style="font-size:18px;"> 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));
}</span>
這個方法也很簡單,要知道的就是ThreadLocal是什麼。
ThreadLocal其實是一個很特殊的類,它可以在不同線程中調用set方法儲存自己的值,并且這些值不會因為在其他線程中進行set操作而改變,目前線程隻能操作目前線程的值。很适合處理多線程時的場景。
而這裡,通過ThreadLocal的get()方法,判斷目前線程的Looper對象是否已經存在,如果存在就回報錯,因為android規定了一個線程隻能調用一次存在一個Looper對象。如果不存在,則建立一個新的looper對象。
我們看一下ThreadLocal的get、set方法。
<span style="font-size:18px;"> public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}</span>
<span style="font-size:18px;"> public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}</span>
可以看到存取的時候都是通過先擷取目前的線程,是以ThreadLocal可以儲存某個線程的值。
回過頭來,我們再看Looper.myLooper()方法:
public static Looper myLooper() {
return sThreadLocal.get();
}
就是通過ThreadLocal擷取目前線程的Looper,判斷Looper.prepared()是否調用,這樣後面的邏輯也想的通了。
順着代碼,我們看一下post()方法,這裡就一次性貼出來了,因為post()、postDelayed()等都是層級傳遞的,比較簡單,最終都是調用的sendMessageAtTime()方法:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
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);
}
這些方法其實都是順着調用下來的,還是比較清楚的。
我們看一下最終調用的enqueueMessage方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
這裡它講本身指派給Message的target,是以 target是一個handler對象,然後調用了MessageQueue的enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
synchronized (this) {
if (mQuitting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
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;
}
這個方法通過無限循環将Message加入到消息隊列,直到沒有Message即msg.next==null為止,這也就是根據先進先出的原則來進行排隊。但是這裡并不是通過隊列來實作,而是通過連結清單,因為我們設定了delay時間,也就是msg.when,這裡要先判斷時間再進行插入,進而排序。到這裡就沒有進行下一級調用了。
接下來,我們看一下Looper.loop()方法,其實它和Looper.prepared()所對應,同樣系統在主線程中幫我們寫好了,是以我們平常不用自己手動調用。
/**
* 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();
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();
}
}
這裡是一個無限循環,首先擷取目前線程的Looper對象,然後再擷取Looper的消息隊列。通過循環将消息隊列依次操作,直到消息隊列為null,跳出循環。那到底是如何調用我們在handler中寫的方法的呢。注意到:
msg.target.dispatchMessage(msg);
而target上面寫道,它就是一個目前Looper的handler:
/**
* 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);
}
}
這裡的實作也不複雜,首先判斷目前Message的callback是否為null,如果是則調用handleCallback方法。
private static void handleCallback(Message message) {
message.callback.run();
}
callback是一個Runnable對象,而這邊就是先判斷runnable是否為null,不為null則直接調用runnable的run方法,否則調用handlerMessage方法,而這個方法就是我們自己要重寫的一個空方法,到這裡我們也就知道了handler是如何調用到run 、handlerMessage方法的。 同時還有幾個結論: 1、如果handler的runnable不為null,即使後面handler重寫了handlerMessage方法依然不會執行; 2、調用handler,如果是在子線程必須加Looper .prepared()、Looper.loop();(一般也不會這麼使用) 3、隻有調用了Looper.loop()方法,消息才會進行處理。
。。。。。
還有很多不足,歡迎大家指出。