Handler、Looper、Message分析_移动开发_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > 移动开发 > Handler、Looper、Message分析

Handler、Looper、Message分析

 2014/12/15 19:26:08  Y_xh  程序员俱乐部  我要评论(0)
  • 摘要:我们都知道,耗时操作不应该在主线程中执行,比如从服务器获取数据然后更新界面。但是,界面更新却只能在主线程中执行。这时,一般都会开启线程获取服务器的数据,然后通过Handler将数据发送到主线程,在主线程中进行界面更新。一般来说我们的做法都是这样:1newThread(newRunnable(){2@Override3publicvoidrun(){4Looper.prepare();5mHandler=newMyHandler();6Messagemsg=newMessage();7msg
  • 标签:Handler 分析

我们都知道,耗时操作不应该在主线程中执行,比如从服务器获取数据然后更新界面。但是,界面更新却只能在主线程中执行。这时,一般都会开启线程获取服务器的数据,然后通过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   @Override
3   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 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。
收工。

发表评论
用户名: 匿名