Handler、Looper、Message分析

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

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

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

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

在handleMessage方法中,就可以处理从线程中发送过来的数据并更新控件(mTextView)了。

其实,我们也可以这样的:

 1         new Thread(new Runnable() {
 2 
 3             @Override
 4             public void run() {
 5                 Looper.prepare();
 6                 Handler handler = new Handler() {
 7                     @Override
 8                     public void handleMessage(Message msg) {
 9                         super.handleMessage(msg);
10                         //Update UI;
11                         mTextView.setText((String)msg.obj);
12                     }
13                 };
14                 Message msg = new Message();
15                 msg.obj = "Hahaahahahahah";
16                 handler.sendMessage(msg);
17                 Looper.loop();
18             }
19 
20         }).start();

这样的话,我们获取数据,更新界面,都是在同一个子线程中执行的。也就是说:“界面更新却只能在主线程中执行”,这句话是有误的。在子线程中,通过Looper,Handler,Message来处理,在子线程中也可以更新界面。至于大量的数据,因为没有实验过,不确认是否也是可行的。而在Activity中,我们不用调用Looper.prepare跟Looper.loop,是因为Activity已经帮我们实现了。

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发送消息,其实就是将消息添加到MessageQueue中。

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 wake
27   // up the event queue unless there is a barrier at the head of the queue
28   // 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.next
42   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 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);

额,也相当长,同样,看重点。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是在主线程中new出来的,handleMessage也就在主线程中执行, 在子线程中new出来,handleMessage就在子线程中执行。
收工。

posted on 2014-12-15 15:19  Y_xh  阅读(279)  评论(2编辑  收藏  举报

导航