Android Handler中的handleMessage方法和post方法之源码剖析 及UI更新方法

我们都知道,在子线程中进行UI操作(更新UI控件)包括以下四种方法:

1.Handler的handlerMessage()方法。

2.Handler的post()方法。

3.View的post()方法。

4.Activity的runOnUiThread()方法。

本文重点分析前两种方法,后面两种稍微说一下。在说第一个方法之前,让我们先来看张图片(图片来源于http://my.oschina.net/keeponmoving/blog/61129)



这个图片在很好的说明了Handler中的handleMessage方法的工作原理(至少说明了怎么回事)。但是这个图有个问题,作者把handleMessage画到了主线程的外面,其实应该是要在主线程里面,这样才能通过handleMessage来操作主线程的ui,接下来详细说明原因:

先看看如何创建handler吧,其构造函数如下:

  1. public Handler() {  
  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 followingHandler class should be static or leaks might occur: " +  
  7.                 klass.getCanonicalName());  
  8.         }  
  9.     }  
  10.     mLooper = Looper.myLooper();  
  11.     if (mLooper == null) {  
  12.         throw new RuntimeException(  
  13.             "Can't create handler insidethread that has not called Looper.prepare()");  
  14.     }  
  15.     mQueue = mLooper.mQueue;  
  16.     mCallback = null;  
  17. }  

可以看到,mLooper = Looper.myLooper();创建了一个新的Looper对象,mLooper不能为空,如果为空则会抛出异常,我们来看下Looper.myLooper()这个方法:

  1. public static finalLooper myLooper() {  
  2.     return (Looper)sThreadLocal.get();  
  3. }  

这个方法功能很简单,就是从sThreadLocal线程中取出Looper对象。如果sThreadLocal线程有Looper对象则返回,如果没有则返回空。那什么时候给sThreadLocal线程设定的Looper对象呢?来看看Looper.prepare()方法吧:

  1. public static finalvoid prepare() {  
  2.     if (sThreadLocal.get() != null) {  
  3.         throw new RuntimeException("Onlyone Looper may be created per thread");  
  4.     }  
  5.     sThreadLocal.set(new Looper());  
  6. }  

其实,我们在使用Handler之前需要调用Looper.prepare()方法,那为什么我们一般在主线程中根本没有调用这个方法啊,其实系统已经帮我们调用了的。ActivityThread中的main()方法源码:

  1. public static void main(String[] args) {  
  2.     SamplingProfilerIntegration.start();  
  3.     CloseGuard.setEnabled(false);  
  4.     Environment.initForCurrentUser();  
  5.     EventLogger.setReporter(newEventLoggingReporter());  
  6.     Process.setArgV0("<pre-initialized>");  
  7.     Looper.prepareMainLooper();  
  8.     ActivityThread thread = new ActivityThread();  
  9.     thread.attach(false);  
  10.     if (sMainThreadHandler == null) {  
  11.         sMainThreadHandler =thread.getHandler();  
  12.     }  
  13.     AsyncTask.init();  
  14.     if (false) {  
  15.         Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));  
  16.     }  
  17.     Looper.loop();  
  18.     throw new RuntimeException("Mainthread loop unexpectedly exited");  
  19. }  

上面代码的第7行就已经调用了Looper.prepare方法(读者可以继续查看Looper.prepareMainLooper()源代码)。
创建好了handler之后,我们再使用的时候需要调用sendMessage()方法,看代码sendMessage():

  1. Message message = newMessage();   
  2. mHandler.sendMessage(message);  

从上面的代码可以看出,每次sendMessage的时候都需要先new一个新的message,比较浪费内存空间。那还有没有更好的办法呢?再来看一下obtainMessage():

  1. public final Message obtainMessage(int what, int arg1, int arg2, Object obj){  
  2.       return Message.obtain(this, what, arg1,arg2, obj);  
  3. }  

这个方法调用了Message的静态方法obtain,来一起看下obtain()方法的源代码:

  1. public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {  
  2.         Message m = obtain();  
  3.         m.target = h;  
  4.         m.what = what;  
  5.         m.arg1 = arg1;  
  6.         m.arg2 = arg2;  
  7.         m.obj = obj;  
  8.         return m;  
  9. }  

又调用了一个无参的obtain()方法,源代码如下:

  1. /* 
  2.      * Return a new Message instance from theglobal pool. Allows us to 
  3.      * avoid allocating new objects in manycases. 
  4. */  
  5. public static Message obtain() {  
  6.         synchronized (sPoolSync) {  
  7.             if (sPool != null) {  
  8.                 Message m = sPool;  
  9.                 sPool = m.next;  
  10.                 m.next = null;  
  11.                 m.flags = 0// clear in-useflag  
  12.                 sPoolSize--;  
  13.                 return m;  
  14.             }  
  15.         }  
  16.         return new Message();  
  17.     }  

原来这个是从消息池中返回的一个message对象,并没有重新创建一个,所以从内存开销角度来说,推荐使用obtainMessage()方法,而不是new一个Message对象。无论是使用new Message()方法还是调用其obtainMessage()方法,都要调用sendMessage()方法,sendMessage()方法都会辗转调用sendMessageAtTime()方法中,其源代码如下所示:

  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis){  
  2.     boolean sent = false;  
  3.     MessageQueue queue = mQueue;  
  4.     if (queue != null) {  
  5.         msg.target = this;  
  6.         sent = queue.enqueueMessage(msg,uptimeMillis);  
  7.     }  
  8.     else {  
  9.         RuntimeException e = newRuntimeException(  
  10.             this + " sendMessageAtTime()called with no mQueue");  
  11.         Log.w("Looper",e.getMessage(), e);  
  12.     }  
  13.     return sent;  
  14. }  

sendMessageAtTime()方法接收两个参数,其中msg参数就是我们发送的Message对象,而uptimeMillis参数则表示发送消息的时间,它的值等于自系统开机到当前时间的毫秒数再加上延迟时间,如果你调用的不是sendMessageDelayed()方法,延迟时间就为0,然后将这两个参数都传递到MessageQueue的enqueueMessage()方法中。这个MessageQueue又是什么东西呢?其实从名字上就可以看出了,它是一个消息队列,用于将所有收到的消息以队列的形式进行排列,并提供入队和出队的方法。而enqueueMessage()就是入队的方法了,源代码如下:

  1. final boolean enqueueMessage(Message msg, long when) {  
  2.         if (msg.when != 0) {  
  3.             throw new AndroidRuntimeException(msg +" This message is already in use.");  
  4.         }  
  5.         if (msg.target == null &&!mQuitAllowed) {  
  6.             throw new RuntimeException("Mainthread not allowed to quit");  
  7.         }  
  8.         synchronized (this) {  
  9.             if (mQuiting) {  
  10.                 RuntimeException e = newRuntimeException(msg.target + " sending message to a Handler on a deadthread");  
  11.                 Log.w("MessageQueue",e.getMessage(), e);  
  12.                 return false;  
  13.             } else if (msg.target == null) {  
  14.                 mQuiting = true;  
  15.             }  
  16.             msg.when = when;  
  17.             Message p = mMessages;  
  18.             if (p == null || when == 0 || when <p.when) {  
  19.                 msg.next = p;  
  20.                 mMessages = msg;  
  21.                 this.notify();  
  22.             } else {  
  23.                 Message prev = null;  
  24.                 while (p != null && p.when<= when) {  
  25.                     prev = p;  
  26.                     p = p.next;  
  27.                 }  
  28.                 msg.next = prev.next;  
  29.                 prev.next = msg;  
  30.                 this.notify();  
  31.             }  
  32.         }  
  33.         return true;  
  34.     }  

通过这个方法名可以看出来通过Handler发送消息实质就是把消息Message添加到MessageQueue消息队列中的过程而已。通过上面遍历等next操作可以看出来,MessageQueue消息队列对于消息排队是通过类似c语言的链表来存储这些有序的消息的。其中的mMessages对象表示当前待处理的消息,然后18到49行可以看出,消息插入队列的实质就是将所有的消息按时间进行排序。既然有入队,那么肯定会有出队的啊,那么出队又是什么鬼样呢?

  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.    }  

可以看到,这个方法从第17行开始,进入了一个死循环,然后不断地调用的MessageQueue的next()方法,我想你已经猜到了,这个next()方法就是消息队列的出队方法。如下所示:

  1. Message next() {  
  2.         // Return here if the message loop has already quit and been disposed.  
  3.         // This can happen if the application tries to restart a looper after quit  
  4.         // which is not supported.  
  5.         final long ptr = mPtr;  
  6.         if (ptr == 0) {  
  7.             return null;  
  8.         }  
  9.   
  10.         int pendingIdleHandlerCount = -1// -1 only during first iteration  
  11.         int nextPollTimeoutMillis = 0;  
  12.         for (;;) {  
  13.             if (nextPollTimeoutMillis != 0) {  
  14.                 Binder.flushPendingCommands();  
  15.             }  
  16.   
  17.             nativePollOnce(ptr, nextPollTimeoutMillis);  
  18.   
  19.             synchronized (this) {  
  20.                 // Try to retrieve the next message.  Return if found.  
  21.                 final long now = SystemClock.uptimeMillis();  
  22.                 Message prevMsg = null;  
  23.                 Message msg = mMessages;  
  24.                 if (msg != null && msg.target == null) {  
  25.                     // Stalled by a barrier.  Find the next asynchronous message in the queue.  
  26.                     do {  
  27.                         prevMsg = msg;  
  28.                         msg = msg.next;  
  29.                     } while (msg != null && !msg.isAsynchronous());  
  30.                 }  
  31.                 if (msg != null) {  
  32.                     if (now < msg.when) {  
  33.                         // Next message is not ready.  Set a timeout to wake up when it is ready.  
  34.                         nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);  
  35.                     } else {  
  36.                         // Got a message.  
  37.                         mBlocked = false;  
  38.                         if (prevMsg != null) {  
  39.                             prevMsg.next = msg.next;  
  40.                         } else {  
  41.                             mMessages = msg.next;  
  42.                         }  
  43.                         msg.next = null;  
  44.                         if (false) Log.v("MessageQueue""Returning message: " + msg);  
  45.                         return msg;  
  46.                     }  
  47.                 } else {  
  48.                     // No more messages.  
  49.                     nextPollTimeoutMillis = -1;  
  50.                 }  
  51.   
  52.                 // Process the quit message now that all pending messages have been handled.  
  53.                 if (mQuitting) {  
  54.                     dispose();  
  55.                     return null;  
  56.                 }  
  57.   
  58.                 // If first time idle, then get the number of idlers to run.  
  59.                 // Idle handles only run if the queue is empty or if the first message  
  60.                 // in the queue (possibly a barrier) is due to be handled in the future.  
  61.                 if (pendingIdleHandlerCount < 0  
  62.                         && (mMessages == null || now < mMessages.when)) {  
  63.                     pendingIdleHandlerCount = mIdleHandlers.size();  
  64.                 }  
  65.                 if (pendingIdleHandlerCount <= 0) {  
  66.                     // No idle handlers to run.  Loop and wait some more.  
  67.                     mBlocked = true;  
  68.                     continue;  
  69.                 }  
  70.   
  71.                 if (mPendingIdleHandlers == null) {  
  72.                     mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];  
  73.                 }  
  74.                 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);  
  75.             }  
  76.   
  77.             // Run the idle handlers.  
  78.             // We only ever reach this code block during the first iteration.  
  79.             for (int i = 0; i < pendingIdleHandlerCount; i++) {  
  80.                 final IdleHandler idler = mPendingIdleHandlers[i];  
  81.                 mPendingIdleHandlers[i] = null// release the reference to the handler  
  82.   
  83.                 boolean keep = false;  
  84.                 try {  
  85.                     keep = idler.queueIdle();  
  86.                 } catch (Throwable t) {  
  87.                     Log.wtf("MessageQueue""IdleHandler threw exception", t);  
  88.                 }  
  89.   
  90.                 if (!keep) {  
  91.                     synchronized (this) {  
  92.                         mIdleHandlers.remove(idler);  
  93.                     }  
  94.                 }  
  95.             }  
  96.   
  97.             // Reset the idle handler count to 0 so we do not run them again.  
  98.             pendingIdleHandlerCount = 0;  
  99.   
  100.             // While calling an idle handler, a new message could have been delivered  
  101.             // so go back and look again for a pending message without waiting.  
  102.             nextPollTimeoutMillis = 0;  
  103.         }  
  104.     }  

它的简单逻辑就是如果当前MessageQueue中存在mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。继续看loop()方法的第31行,每当有一个消息出队,就将它传递到msg.target的dispatchMessage()方法中,那这里msg.target又是什么呢?其实这个msg.target就是上面分析Handler发送消息代码部分Handler的enqueueMessage方法中的msg.target = this;语句,也就是当前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. }  
如果mCallback不为空,则会调用handleMessage函数,这个有没有很熟悉?没错,就是我们自己在 主线程中写的那个handleMessage方法了,从这段代码中也可以看出,之前sendMessage的参数也被传到了handleMessage方法中了。所以,一个标准的异步消息处理的函数应该就是这样子的:
  1. class LooperThread extends Thread {  
  2.       public Handler mHandler;  
  3.   
  4.       public void run() {  
  5.           Looper.prepare();  
  6.   
  7.           mHandler = new Handler() {  
  8.               public void handleMessage(Message msg) {  
  9.                   // process incoming messages here  
  10.               }  
  11.           };  
  12.   
  13.           Looper.loop();  
  14.       }  
  15.   }  
好了,第一种方法已经说完了,接下来说说post方法,我们在使用的时候需要传进一个Runnable参数:
  1. handler.post(new Runnable() {  
  2.            @Override  
  3.            public void run() {  
  4.                //do something  
  5.            }  
  6.        });  
我们来看下post的源码吧:
  1. public final boolean post(Runnable r){  
  2.    return  sendMessageDelayed(getPostMessage(r), 0);  
  3. }  
返回一个sendMessageDelayed方法,有没有觉得很熟悉?至少sendMessageAtTime(上面已经分析过这个源码了)很熟吧,其实 sendMessageDelayed就是调用的sendMessageAtTime方法,源代码如下:
  1. public final boolean sendMessageDelayed(Message msg, long delayMillis) {  
  2.         if (delayMillis < 0) {  
  3.             delayMillis = 0;  
  4.         }  
  5.         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  6. }  
那我们再来看看sendMessageDelayed的第一个参数getPostMessage(r)的原理,其实从上面两段代码就可以看出getPostMessage(r)就是将一个Runnable对象转换为一个Message对象,如下所示:
  1. private final Message getPostMessage(Runnable r) {  
  2.     Message m = Message.obtain();  
  3.     m.callback = r;  
  4.     return m;  
  5. }  

在这个方法中将消息的callback字段的值指定为传入的Runnable对象。咦?这个callback字段看起来有些眼熟啊,喔!在Handler的dispatchMessage()方法中原来有做一个检查,如果Message的callback等于null才会去调用handleMessage()方法,否则就调用handleCallback()方法。那我们快来看下handleCallback()方法中的代码吧:

  1. private final void handleCallback(Message message) {  
  2.     message.callback.run();  
  3. }  

调用的run方法就是我们通过post来重写的Runnable对象的一个方法,WTF!!这也太简单了吧,好了,总结下:通过post()方法在Runnable对象的run()方法里更新UI的效果完全和在handleMessage()方法中更新UI原理完全相同,特别强调这个Runnable的run方法还在当前线程中阻塞执行,没有创建新的线程。

那么我们还是要来继续分析一下,为什么使用异步消息处理的方式就可以对UI进行操作了呢?这是由于Handler总是依附于创建时所在的线程,比如我们的Handler是在主线程中创建的,而在子线程中又无法直接对UI进行操作,于是我们就通过一系列的发送消息、入队、出队等环节, 最后调用到Handler的handleMessage方法中或者handleCallback方法中,这时的handleMessage方法和handleCallback已经是在主线程中运行的,因而我们当然可以在这里进行UI操作了。最后来看下View的post方法:

  1. public boolean post(Runnable action) {  
  2.     Handler handler;  
  3.     if (mAttachInfo != null) {  
  4.         handler = mAttachInfo.mHandler;  
  5.     } else {  
  6.         ViewRoot.getRunQueue().post(action);  
  7.         return true;  
  8.     }  
  9.     return handler.post(action);  
  10. }  
Activity中的runOnUiThread()方法,如下所示:
  1. public final void runOnUiThread(Runnable action) {  
  2.     if (Thread.currentThread() != mUiThread) {  
  3.         mHandler.post(action);  
  4.     } else {  
  5.         action.run();  
  6.     }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值