handler淺析
-
基本概念
handler在創建的時候,就會和當前創建它的線程所綁定。一個線程可以有多個handler,但是只能有一個looper對象和MessageQueue
熟悉Windows編程的朋友知道Windows程序是消息驅動的,并且有全局的消息循環系統。Google參考了Windows的消息循環機制,也在Android系統中實現了消息循環機制。Android通過Looper、Handler來實現消息循環機制。Android的消息循環是針對線程的,每個線程都可以有自己的消息隊列和消息循環。
Android系統中的Looper負責管理線程的消息隊列和消息循環。通過Looper.myLooper()得到當前線程的Looper對象,通過Looper.getMainLooper()得到當前進程的主線程的Looper對象。
官方解釋
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. ==When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it== -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
源碼
private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
Thread currentThread = Thread.currentThread();
if (group == null) {
group = currentThread.getThreadGroup();
}
if (group.isDestroyed()) {
throw new IllegalThreadStateException("Group already destroyed");
}
this.group = group;
synchronized (Thread.class) {
id = ++Thread.count;
}
if (threadName == null) {
this.name = "Thread-" + id;
} else {
this.name = threadName;
}
this.target = runnable;
this.stackSize = stackSize;
this.priority = currentThread.getPriority();
this.contextClassLoader = currentThread.contextClassLoader;
// Transfer over InheritableThreadLocals.
if (currentThread.inheritableValues != null) {
inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues);
}
// add ourselves to our ThreadGroup of choice
this.group.addThread(this);
}
-
與looper的交互
一個線程里只能有一個looper對象,消息的循環,一定需要looper,而UI主線程里的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 prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
prepare()里會檢測當前線程中是否有looper對象,有會報錯( throw new RuntimeException("Only one Looper may be created per thread");),沒有則創建。而在loop()里,則會得到當前looper的queue,執行消息循環
源代碼
prepare()
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();
}
loop()
/**
* 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.recycleUnchecked();
}
}
-
總結
- handler在創建時和線程綁定
- 每個線程里面只有一個looper和MessageQueue,可以有多個handler。
- 主線程(UI線程)的looper對象在activity創建時,Looper和MessageQueue在ActivityThread.main時已經創建好。
- 其他線程(非主線程),想要使用消息循環,必須使用Looper.prepair()以及Looper.loop()
hanlder雖然是綁定線程的,但是可以在其他線程中使用這個handler,利用這個handler傳遞消息,當然這個消息就到了創建這個handler的線程里了。looper是線程安全。如圖:
這2個handler屬于UI線程