Android Handler中的handleMessage方法和post方法之源码剖析

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

1.Handler的handlerMessage()方法。

2.Handler的post()方法。

3.View的post()方法。

4.Activity的runOnUiThread()方法。

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

Android Handler中的handleMessage方法和post方法之源码剖析_第1张图片


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

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

public Handler() {
    if (FIND_POTENTIAL_LEAKS) {
        final Class klass= getClass();
        if ((klass.isAnonymousClass() ||klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() &Modifier.STATIC) == 0) {
            Log.w(TAG, "The followingHandler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler insidethread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = null;
}

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

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

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

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

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

public static void main(String[] args) {
    SamplingProfilerIntegration.start();
    CloseGuard.setEnabled(false);
    Environment.initForCurrentUser();
    EventLogger.setReporter(newEventLoggingReporter());
    Process.setArgV0("");
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    if (sMainThreadHandler == null) {
        sMainThreadHandler =thread.getHandler();
    }
    AsyncTask.init();
    if (false) {
        Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));
    }
    Looper.loop();
    throw new RuntimeException("Mainthread loop unexpectedly exited");
}

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

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

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

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

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

public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;
        return m;
}

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

/*
     * Return a new Message instance from theglobal pool. Allows us to
     * avoid allocating new objects in manycases.
*/
public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-useflag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

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

public boolean sendMessageAtTime(Message msg, long uptimeMillis){
    boolean sent = false;
    MessageQueue queue = mQueue;
    if (queue != null) {
        msg.target = this;
        sent = queue.enqueueMessage(msg,uptimeMillis);
    }
    else {
        RuntimeException e = newRuntimeException(
            this + " sendMessageAtTime()called with no mQueue");
        Log.w("Looper",e.getMessage(), e);
    }
    return sent;
}

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

final boolean enqueueMessage(Message msg, long when) {
        if (msg.when != 0) {
            throw new AndroidRuntimeException(msg +" This message is already in use.");
        }
        if (msg.target == null &&!mQuitAllowed) {
            throw new RuntimeException("Mainthread not allowed to quit");
        }
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = newRuntimeException(msg.target + " sending message to a Handler on a deadthread");
                Log.w("MessageQueue",e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }
            msg.when = when;
            Message p = mMessages;
            if (p == null || when == 0 || when 

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

 /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

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

Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

它的简单逻辑就是如果当前MessageQueue中存在mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。继续看loop()方法的第31行,每当有一个消息出队,就将它传递到msg.target的dispatchMessage()方法中,那这里msg.target又是什么呢?其实这个msg.target就是上面分析Handler发送消息代码部分Handler的enqueueMessage方法中的msg.target = this;语句,也就是当前Handler对象。所以接下来的重点自然就是回到Handler类看看我们熟悉的dispatchMessage()方法,如下:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

如果mCallback不为空,则会调用handleMessage函数,这个有没有很熟悉?没错,就是我们自己在 主线程中写的那个handleMessage方法了,从这段代码中也可以看出,之前sendMessage的参数也被传到了handleMessage方法中了。所以,一个标准的异步消息处理的函数应该就是这样子的:
class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }
好了,第一种方法已经说完了,接下来说说post方法,我们在使用的时候需要传进一个Runnable参数:

 handler.post(new Runnable() {
            @Override
            public void run() {
                //do something
            }
        });

我们来看下post的源码吧:

public final boolean post(Runnable r){
   return  sendMessageDelayed(getPostMessage(r), 0);
}

返回一个sendMessageDelayed方法,有没有觉得很熟悉?至少sendMessageAtTime(上面已经分析过这个源码了)很熟吧,其实 sendMessageDelayed就是调用的sendMessageAtTime方法,源代码如下:
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
那我们再来看看sendMessageDelayed的第一个参数getPostMessage(r)的原理,其实从上面两段代码就可以看出getPostMessage(r)就是将一个Runnable对象转换为一个Message对象,如下所示:
private final Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

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

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

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

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

public boolean post(Runnable action) {
    Handler handler;
    if (mAttachInfo != null) {
        handler = mAttachInfo.mHandler;
    } else {
        ViewRoot.getRunQueue().post(action);
        return true;
    }
    return handler.post(action);
}

Activity中的runOnUiThread()方法,如下所示:

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

参考文章:

Android异步消息处理机制详解及源码分析

Android异步消息处理机制完全解析,带你从源码的角度彻底理解








你可能感兴趣的:(安卓,java)