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中处理该消息的效果了,是不是很巧妙。

posted @ 2016-03-30 15:49  gatsbydhn  阅读(210)  评论(0编辑  收藏  举报