关于handler消息处理机制,只要一提到,相信作为一个android工程师,脑海就会有这么一个流程
大家都滚瓜烂熟了,但别人问到几个问题,很多人还是栽到这个“烂”上面,比如:
- 一个线程是如何对应一个Lopper的?
- messageQueue是如何做到线程安全的?
首先先Looper看一段代码:
private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}
这里先通过sThreadLocal.get()去获取这个Looper, 获取不到再去通过new (Looper(quitAllowed))去创建Looper()对象。再来看看这个构造函数:
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}
构造函数私有的,只能通过Prepare去初始化,这里就形成了一个关系,一个Looper 对应一个MessageQueue。
回过头再来分析,线程是如何和Looper对应的。
/*** Return the Looper object associated with the current thread. Returns* null if the calling thread is not associated with a Looper.*/public static @Nullable Looper myLooper() {return sThreadLocal.get();}
在Prepare()的时候给sThreadLocal.set(Looper), 获取Looper的对象是通过sThreadLocal.get()返回的,下面看看sThreadLocal:
// sThreadLocal.get() will return null unless you've called prepare().static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
这是一个静态成员变量所有的Looper对象共有属性。
sTreadLocal保存线程的唯一值,这样就保证一个线程对应一个looper.
至于MessageQueue的线程同步,会有奇葩的人问到,无非就是syncnized关键字,或者加锁。
boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {if (mQuitting) {IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");Log.w(TAG, e.getMessage(), e);msg.recycle();return false;}msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;needWake = mBlocked;} else {// Inserted within the middle of the queue. Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}
这里用的是一个加锁的方式。
在Loop.loop中有一死循环,那么当没有消息时,主线程不可能死掉吧?
“`
在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
还有什么关于handler的问题,希望大家可以提出来。