Android进阶-消息机制
Handler的主要作用是将一个任务切换到某个指定的线程去执行。
Android的消息机制主要涉及三个类:Handler, Looper, MessageQueue:
现在假设一个情景:
有两个线程,线程1和线程2,在线程1中调用Looper.prepare(), 创建一个Handler对象handler,调用Looper.loop()。
在线程2中调用handler.sendMessage()发送消息,那最终这个消息会在线程1中被handler.handleMassage()处理。我们画出内存模型帮助理解:
从根据这个调用顺序来看看这个模型:
1.Looper在线程1中调用prepare()方法:
1 private static void prepare(boolean quitAllowed) { 2 if (sThreadLocal.get() != null) { 3 throw new RuntimeException("Only one Looper may be created per thread"); 4 } 5 sThreadLocal.set(new Looper(quitAllowed)); 6 }
这里的sTreadLocal是Looper的static对象:
1 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
ThreadLocal对象在不同的线程中会指向不同的Looper对象(java编程思想中有介绍)或者说在不同线程调用sThreadLocal.get()返回的是个该线程关联的Looper对象,使用get(), set()访问。prepare()为线程1创建一个Looper对象,来看看构造方法:
1 private Looper(boolean quitAllowed) { 2 mQueue = new MessageQueue(quitAllowed); 3 mThread = Thread.currentThread(); 4 }
Looper内部封装了一个MessageQueue对象。
2.在线程1中创建handler对象:
1 public Handler(Callback callback, boolean async) { 2 if (FIND_POTENTIAL_LEAKS) { 3 final Class<? extends Handler> klass = getClass(); 4 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 5 (klass.getModifiers() & Modifier.STATIC) == 0) { 6 Log.w(TAG, "The following Handler class should be static or leaks might occur: " + 7 klass.getCanonicalName()); 8 } 9 } 10 11 mLooper = Looper.myLooper(); 12 if (mLooper == null) { 13 throw new RuntimeException( 14 "Can't create handler inside thread that has not called Looper.prepare()"); 15 } 16 mQueue = mLooper.mQueue; 17 mCallback = callback; 18 mAsynchronous = async; 19 }
从第11行看出Handler中封装了线程1的Looper对象,和消息队列,也就是说handler对象可以在线程2中访问线程1的消息队列,从而向里面添加新消息(后面将看到),我们从内存模型也可以理解这点。但是这个Handler可以被任意线程访问,这就是关键所在。
3.在线程1中调用Looper.loop():
1 /** 2 * Run the message queue in this thread. Be sure to call 3 * {@link #quit()} to end the loop. 4 */ 5 public static void loop() { 6 final Looper me = myLooper(); 7 if (me == null) { 8 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 9 } 10 final MessageQueue queue = me.mQueue; 11 12 // Make sure the identity of this thread is that of the local process, 13 // and keep track of what that identity token actually is. 14 Binder.clearCallingIdentity(); 15 final long ident = Binder.clearCallingIdentity(); 16 17 for (;;) { 18 Message msg = queue.next(); // might block 19 if (msg == null) { 20 // No message indicates that the message queue is quitting. 21 return; 22 } 23 24 // This must be in a local variable, in case a UI event sets the logger 25 Printer logging = me.mLogging; 26 if (logging != null) { 27 logging.println(">>>>> Dispatching to " + msg.target + " " + 28 msg.callback + ": " + msg.what); 29 } 30 31 msg.target.dispatchMessage(msg); 32 33 if (logging != null) { 34 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 35 } 36 37 // Make sure that during the course of dispatching the 38 // identity of the thread wasn't corrupted. 39 final long newIdent = Binder.clearCallingIdentity(); 40 if (ident != newIdent) { 41 Log.wtf(TAG, "Thread identity changed from 0x" 42 + Long.toHexString(ident) + " to 0x" 43 + Long.toHexString(newIdent) + " while dispatching to " 44 + msg.target.getClass().getName() + " " 45 + msg.callback + " what=" + msg.what); 46 } 47 48 msg.recycleUnchecked(); 49 } 50 }
loop()获取当前线程的Looper对象,开启无限循环调用MessageQueue.next不断获取Message,msg.target.dispatchMessage(msg);msg.target就是handler对象(下面将看到),我们看看该方法:
1 public void dispatchMessage(Message msg) { 2 if (msg.callback != null) { 3 handleCallback(msg); 4 } else { 5 if (mCallback != null) { 6 if (mCallback.handleMessage(msg)) { 7 return; 8 } 9 } 10 handleMessage(msg); 11 } 12 }
第10行调用handlerMessage()方法,一定是在线程1中执行。
4.在线程2中调用handler.sendMessage():
1 public final boolean sendMessage(Message msg) 2 { 3 return sendMessageDelayed(msg, 0); 4 }
该方法最终调用:
1 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 2 MessageQueue queue = mQueue; 3 if (queue == null) { 4 RuntimeException e = new RuntimeException( 5 this + " sendMessageAtTime() called with no mQueue"); 6 Log.w("Looper", e.getMessage(), e); 7 return false; 8 } 9 return enqueueMessage(queue, msg, uptimeMillis); 10 }
第9行方法:
1 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { 2 msg.target = this; 3 if (mAsynchronous) { 4 msg.setAsynchronous(true); 5 } 6 return queue.enqueueMessage(msg, uptimeMillis); 7 }
第2行将handler对象保存到msg.target;
第6行,因为handler有线程1MessageQueue对象的引用,这里向该消息队列增加消息。
5.当线程切换到线程1时,loop循环不断获取message对象,调用msg.target.dispatchMessage(msg);在线程1中处理该消息。这样就实现了,在线程2中调用sendMessage(),在线程1中处理该消息的效果了,是不是很巧妙。