首页 > 代码库 > Handler、Looper、Message分析

Handler、Looper、Message分析

我们都知道,耗时操作不应该在主线程中执行,比如从服务器获取数据然后更新界面。但是,界面更新却只能在主线程中执行。这时,一般都会开启线程获取服务器的数据,然后通过Handler将数据发送到主线程,在主线程中进行界面更新。一般来说我们的做法都是这样:

 1 new Thread(new Runnable() { 2   @Override 3   public void run() { 4     Looper.prepare(); 5     mHandler = new MyHandler(); 6     Message msg = new Message(); 7     msg.obj = "Text"; 8     mHandler.sendMessage(msg); 9     Looper.loop();10   }11 }).start();

 

MyHandler继承Handler,并且复写了handleMessage(Message msg)方法,代码如下:

1 class MyHandler extends Handler{2   @Override3   public void handleMessage(Message msg) {4     String text = (String)msg.obj;5     mTextView.setText(text);6   }7 }

 


在handleMessage方法中,就可以处理从线程中发送过来的数据并更新控件(mTextView)了。知道怎么用,有个蛋用啊,得知道其原理啊(衰!!!)。那就来看源码吧。

1)来看看Looper.prepare做了什么?
prepare()方法中调用了其重载方法,并传入了参数true。

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 }

 

sThreadLocal是ThreadLoacl是对象,关于ThreadLocal,只需要知道ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。这里的sThreadLocal保存的是Looper对象。一个线程中最多只能有一个Looper,并且只能在prepare方法中创建。所以一个线程中最多只能调用一次Looper.prepare,否则就会抛出RuntimeException("Only one Looper may be created per thread")。
sThreadLocal.set(new Looper(quitAllowed)); new出了一个Looper并且将其添加进sThreadLocal中。
2)Looper的构造方法中做了什么?

1 private Looper(boolean quitAllowed) { 2   mQueue = new MessageQueue(quitAllowed);3   mRun = true;4   mThread = Thread.currentThread();5 }

MessageQueue是一个先进先出的消息队列,我们通过handler发送的消息就是由其。

3)发送消息
现在MessageQueue已经有了,就等消息发送过来了。通过handler.sendMessage方法发送的消息,最终都会进入到下面这个方法中:

 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 }

 

mQueue是在构造方法中通过获得当前线程的Looper来获取的。enqueueMessage,最终其实现是调用MessageQueue.enqueueMessage来实现,就是将消息添加到队列中,来是怎么实现的。

 1 final boolean enqueueMessage(Message msg, long when) { 2   if (msg.isInUse()) { 3     throw new AndroidRuntimeException(msg + " This message is already in use."); 4   }  5   if (msg.target == null) { 6     throw new AndroidRuntimeException("Message must have a target."); 7   } 8  9   boolean needWake;10   synchronized (this) {11   if (mQuiting) {12     RuntimeException e = new RuntimeException(13     msg.target + " sending message to a Handler on a dead thread");14     Log.w("MessageQueue", e.getMessage(), e); 15     return false;16   }17 18   msg.when = when; 19   Message p = mMessages;20   if (p == null || when == 0 || when < p.when) {21     // New head, wake up the event queue if blocked.22     msg.next = p;23     mMessages = msg;24     needWake = mBlocked;25   } else {26     // Inserted within the middle of the queue. Usually we don‘t have to wake27   // up the event queue unless there is a barrier at the head of the queue28   // and the message is the earliest asynchronous message in the queue.29   needWake = mBlocked && p.target == null && msg.isAsynchronous();30   Message prev;31   for (;;) {32     prev = p;33     p = p.next;34     if (p == null || when < p.when) {35       break;36     }37     if (needWake && p.isAsynchronous()) {38       needWake = false;39     }40   }41   msg.next = p; // invariant: p == prev.next42   prev.next = msg;43   }44 }45   if (needWake) {46     nativeWake(mPtr);47   }48   return true;49 }

方法有点长,挑重点看。消息的添加,其实就是在if else中这一段代码中实现的。Message,是琏表。知道了这一点,其实上面的重点代码也就不难理解了。当前队列为空,或者when(从开机到现在的毫秒数,加上delay)为0,或者当前的消息的时间比前一个消息的时间小,都会被判断为当前队列中没有消息。代码会进入到if片段。当队列中有消息,进入到else,通过琏表添加元素的方式,把消息添加到队列中。
OK,发送消息,把消息添加进队列的都已经完成了,那消息是如何从队列中取出来,并交给handler处理的呢?
4)Looper.loop()
从队列中取出消息,并交给handler处理,都在这里面了。

 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 block19             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 logger25             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);

额,也相当长,同样,看重点。Message msg = queue.next();这个,用脚趾头想也是从队列中取出消息。不看了。 msg.target.dispatchMessage(msg);msg.target,就是handler。
handler.dispatchMessage如下:

 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     }   

这个,看到了我们熟悉的handleMessage。
收工。

Handler、Looper、Message分析