类似于iOS以及其他桌面平台的Runloop模型,Android也是这种处理方式。从原来看gtk/mfc开始,后来学习Linux编程中的IO模型和node.js,还有一些第三方类库libuv/libevent,为了简化多线程模型,基本都是采用这种方式解决。这样带来的好处太大了,省却了非常多的并发开销,充分利用CPU。刚接触Andorid开发,官方教程中说有三种在主线程中执行代码的方式,但最后其实都是一种,looper + handler + message实现的。做Android开发比iOS开发最好的一点是,随时可以查看源代码
先从Messge对象说起。看的一本书中说Message对象是可以recycle的,通过static factory methods创建,这样可以减轻GC的压力。获取Message的话不是通过new,而是通过Message对象的obtain方法,这个方法和recycle方法配合可以实现Message对象重用。大量使用Message进行调用,并不会引起性能上的负担,这点尤其好。Message对象里面的属性很多,我个人觉得对粗略理解整个过程有用的属性是:
根据我的理解,Message对象简单来说就是一个可复用的callback代码封装,用于在Handler与Looper之间进行传递回掉信息,用于looper循环中调用,这里面的callback代码会在looper中通过Handler被调用。
我认为Handler是整个looper机制的核心。如果要在主线程中执行代码,可以通过简单地调用View.post(Runnable ction)来进行调用,我一直很像知道内部是如何实现的,Android开发就是好,看看源代码就行,发现里面是把Runnable传递给Handler的post方法,post方法会把Runnable封装为一个Message对象,再放入Looper的MessageQueue中等待looper循环获取执行。
public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); return true; }
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
注意一下,这里会将handler对象的指针传递给Message的target成员,后面会使用这个指针。这里最主要的核心是queue,也就是looper的MessageQueue对象,这个对象管理所有的Message,并且这个对象线程安全,而且内部的处理可以进行延迟调用的处理。looper中获取要执行的Message,也是通过queue这个对象获取的,如果说Handler是Looper和Messge执行的中间对象,那MessageQueue就是数据结构中间对象。
上面主要讲的是将Message放入到MessageQueue中,等待looper执行,而Message执行调用代码也是通过Handler执行的,代码如下:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
这里面,首先判断Message对象的callback即Runnable是否设置,如果设置的话,直接进行Runnable的run()方法。没有设置的话,则判断Handler对象的mCallback是否设置,如果设置的话,则进行回掉。如果都没有设置,则会调用自身的handleMessage方法,如果是Hanlder的子类,则可以覆盖这个方法进行处理。如果都没有情况下,则该message不会进行处理。
Looper是具体执行做代码执行的方法,里面在线程中进行for(;;)循环,通过MessageQueue获取Message对象,然后再进行回调。
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.recycle(); }
上面的代码比较简单,下面说几个觉得需要注意的
1. msg.target.dispatchMessage(msg)这一行,是Message通过Handler对象进行回调,这样就把整个过程连接起来了。其实我一直在疑惑为什么不直接调用msg.callback.run()进行回调,而是要通过Handler进行dispatch,看了代码之后才明白,Handler本身也有个Callback接口对象,这个多一种情况能处理Message回调,可以处理一些只有what字段的简单的empty message,看过HanlderThread的Demo就是这样的。同时,我觉得这样可以进行大门功能分层,Message被限制为传递消息的对象,而Handler才是具体执行的对象,模块功能划分比较清晰。
2. 经同事提醒,觉得queue.next()方法应该重点提一下,这里如果queue为空的情况下,会发生等待。看了一下next()里面的代码,是使用一个nativePollOnce的JNI调用保持等待的,猜测里面应该是使用epoll之类的POSIX IO模型实现,这样可以节省大量的CPU资源。唤醒使用通过JNI调用nativeWake方法来进行的。
3. 最后一句msg.recycle()方法,这句就是实现Message对象重复使用的代码,在Android Pushing the limits书中有讲到
由于刚开始做Android,上面很多理解肯定是有偏差的,也没有仔细阅读代码进行学习,这个只是为了保证自己对Android开发的运行机制有个粗略的了解,以便遇到问题无法解决