Handler、Looper和MessageQueue三兄弟的解读分析

前言

最近在看Android源码,从Application层到framework层,再到JNI层、Native层。Java和Native之间的配合是有很大的学问的,这次举个简单的例子(Handler、Looper和MessageQueue三兄弟),介绍一下它们从Java一直到Native的使用和实现。

Handler机制

Handler的使用

Android中规定,只有主线程可以更改UI,而很多耗时操作为了防止ANR,我们又不得不放在子线程中操作。为了解决这组矛盾,我们需要一种可靠的跨线程通讯机制,而Android提供给我们一种方法,那就是handler。
而提到handler,我们就不得不提到Looper和MessageQueue。handler负责发送消息,MessageQueue接受消息并维护,Looper则无限循环来检查MessageQueue中是否存在待处理的消息,并将它们发给对应的handler,由handler来处理。但是,虽然由handler开始,由handler结束,可是它们已经是在不同线程中了。
这么说可能比较抽象,举个具体一点的例子:

//demo1
//主线程
public class MainActivity()extends Activity{
	……
	private Handler handler = new Handler(){
		@Override 
		public void handleMessage(Message msg){
       			//接到消息后的处理逻辑
       		}
	}
	……
	//子线程
	new Thread(){
		@Override 
		public run(){
		handler.sendMessage(msg);
		}
	}
}

在这段代码中,我们建立一个子线程,并发送了一条消息,这条消息最终将由handleMessage()进行处理,这样我们就完成了一次跨线程的通讯了。

Handler机制的实现

Handler、Looper和MessageQueue之间的关系

demo1的整个过程中我们似乎没有看到Looper和MessageQueue的身影,但其实它们隐于幕后且功不可没,接下来我们就介绍本次通讯的底层实现。
首先,在我们构造一个Handler的时候,Handler的内部就会拿取当前线程的Looper对象,拿取不到就会报错。我们可以看一下源码:

/**
     * Default constructor associates this handler with the {@link Looper} for the
     * current thread.
     *
     * If this thread does not have a looper, this handler won't be able to receive messages
     * so an exception is thrown.
     */
/**
     *我翻译了一下:
     *默认构造函数会把当前线程的Looper和本handler连接起来
     *如果当前线程没有一个Looper,handler将无法接受到消息,所以会报错
     */
public Handler() {
        this(null, false);
}
public Handler(Callback callback, boolean async) {
	......
        mLooper = Looper.myLooper();         //myLooper返回当前线程的Looper对象
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        ......
        mQueue = mLooper.mQueue;      //注释1
}

很明显,在使用Handler的默认构造方法时,将调用Looper类的静态方法myLooper(),该方法返回当前线程的Looper对象,如果当前线程没有Looper就会返回NULL,进而会抛出异常。所以,我们可以得出结论,在构造一个Handler对象时,必须要向Handler.mLooper成员赋值,而一般都是当前线程的Looper对象(注:每个线程只允许有一个Looper对象)。
那为何我们的前面的demo1代码中并没有声明一个Looper呢?因为为了方便编程,Android在创建主线程的时候,自动帮我们实现了一个Looper实例

//AcitivityThread.java 
//Activity从这里启动
public static void main(String[] args) {
	......
	Looper.prepareMainLooper();      //在主线程中创建looper对象
	......
}

所以,主线程中我们不需要手动添加Looper对象了。但是,如果想在子线程中接受消息,我们就必须要创建一个Looper对象,创建的方法也很简单,Looper类内部已经帮我们封装好了一个方法Looper.prepare()

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

还没完,当我们构造Looper的时候,Looper有一个MessageQueue类型的成员变量mQueue,即我们的MessageQueue是由Looper来维护的,且每个Looper都有它自己的MessageQueue。

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
}

不仅如此,在前文的注释1处,我们可以发现很有意思的一件事,Handler也有一个叫mQueue 的成员变量,而且它就是由Looper对象的mQueue 赋值的。

mQueue = mLooper.mQueue;

至此,我们已经理清了Handler、Looper和MessageQueue之间的关系:

  • Handler需要一个对应Looper才能构造,且在构造时会将Looper对象保存到成员变量mLooper中,还会将Looper的mQueue复制到自己的成员变量mQueue中。
  • Looper内部有一个MessageQueue对象,这个对象会随Looper的创建而创建,随着Looper的消亡而消亡。

用一段代码描述就是

class Hander{
	Looper mLooper;
	MessageQueue mQueue;
}
class Looper{
	MessageQueue mQueue;
}

Message的传递流程

Handler发送消息

理解了handler三兄弟的关系之后,我们再来看message的传递流程就简单很多了。首先,我们假设B线程向A线程发送messsage,那么我们应该在B线程中调用Handler的send方法。

//A线程
public class A(){
	……
	Looper.prepare();
	private Handler handler = new Handler(){
		@Override 
		public void handleMessage(Message msg){
       			//接到消息后的处理逻辑
       		}
	}
	……
	//B线程
	new Thread(){
		@Override 
		public run(){
		handler.sendMessage(msg);
		}
	}
}

Handler中有一系列send方法,根据场景和Message对象的不同使用不同的方法,但它们的传递原理是一致的。这里,我们以sendMessage(Message msg)为例,上源码:

//android.os.Handler
public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
	......
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
	MessageQueue queue = mQueue;       //看这里
	......
        return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, 
	long uptimeMillis) {
	......
        return queue.enqueueMessage(msg, uptimeMillis);
}

我们绕来绕去,几经波折最会发现,其实是调用了queue.enqueueMessage(msg, uptimeMillis)方法,而queue是前面传进来的mQueue对象。所以其实就是handler调用了MessageQueue的enqueueMessage方法。至此,handler的前半部分任务完成了,我们接着来看MessageQueue。

//android.os.MessageQueue
boolean enqueueMessage(Message msg, long when) {
	......
	synchronized (this) {
		//判断MessageQueue是否在退出
        	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;
            	}

		//将当前message插入队列中
		//该队列根据是有序队列,根据message的when从小到大进行排序
		//根据当前message的when,将它插入合适的地方
            	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.next
                	prev.next = msg;
            	}

            	// We can assume mPtr != 0 because mQuitting is false.
           	if (needWake) {
                	nativeWake(mPtr);
            	}
        }
        return true;
}

学过单链表的朋友会发现,这段代码很眼熟。没错,这不就是单链表吗。为了方便插入、查询等操作,MessageQueue采用的就是单链表的数据结构。因为message类是自带next指针,MessageQueue中只保留了整个队列的头部,也就是第一个消息,将其存放在mMessage变量中。因为message可能有延时的需求,故而将所有message按照时间上的先后进行排序,以便执行的时候我们可以按时间顺序执行。到这里,我们成功的将一条消息发送至MessageQueue中。
细心的朋友会发现,其实我们已经跨越线程,从B线程到A线程之中了。我们的MessageQueue对象是Looper帮我们构造并存在Looper内部的,而Looper又是在A线程中的构造的,所以MessageQueue中的Message已经来到了A线程了。
有读者肯定已经晕菜了,怎么就这样了呢。其实关键很简单,我们在创建B的线程时,似乎这个B线程是空的,什么也没有。其实不是这样的,我们在B线程的run()方法中调用了名为handler的变量,但是B线程中并没有声明啊,它是哪来的呢?是A线程中带过去的,相当于在创作B线程的时候,我们给了它一份handler的拷贝作为索引,在需要发送消息时,我们就可以根据这个索引找到目的地了。

Looper监听消息队列

到这里,Handle机制讲完一半了。可能有点读者好奇,不是已经跨过线程了吗,为什么还有这么多内容?而且Looper和MessageQueue好像也没什么用处的样子。不急,我们刚才举的例子,是只有两个线程的情况,可实际工程中,会有很多很多线程同时向主线程发送message,如果不加以约束的话,会出现很多很多问题。

  • 我们不知道子线程的message什么时候会发过来
  • 如果瞬间接到多个message,又无法同时处理,可能导致丢失

为了解决这两个问题,Android设计了Looper和MessageQueue。
Looper的核心是loop()函数,上源码:

//Looper.java
public static void loop() {
        ......
        for (;;) {
            Message msg = queue.next();       // might block
            if (msg == null) {
                return;
            }
            ......
            msg.target.dispatchMessage(msg);
            ......
            msg.recycleUnchecked();
        }
    }

Looper的源码很长,但是我们现在只需要关注这些。我们可以看到,Looper的本质上就是个死循环,永远不停的等待着,从MessageQueue中拿取message对象。拿取的方式也很简单,即调用MessageQueue中调用next()函数就好。这就和前文所说的Handler的sendMessage()联系上了,Handler发送到MessageQueue中的message就是在这里被拿出来处理的。

MessageQueue的next()

我们再来看看MessageQueue的next()方法:

//MessageQueue.java
Message next() {
        final long ptr = mPtr;     //获得nativeMessageQueue*
        if (ptr == 0) {
            return null;
        }
        ......
        for (;;) {     //注意这里,这说明next()也是个死循环,可能会阻塞
        nativePollOnce(ptr, nextPollTimeoutMillis);     //进行一次轮询
        synchronized (this) {
        	// Try to retrieve the next message.  Return if found.
        	//查找下一个需要处理的message
        	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) {  //有需要处理的message
	                if (now < msg.when) { 
	                //下一个需要处理的message还需要延时等待
                        // 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 {
			//下一个需要处理的message不需要等待,立即处理
                        	// Got a message.
                        	mBlocked = false;
                        	if (prevMsg != null) {
                        	//被处理的message不在队首
                        	prevMsg.next = msg.next;
                        	} else {
                        	//被处理的message在队首
                        		mMessages = msg.next;
                        	}
                        	msg.next = null;
                        	if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        	msg.markInUse();
                        	return msg;
                        }
		} else {
			// No more messages.
			//没有需要处理的message
			nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                //该MessageQueue正在退出
                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(TAG, "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;
        }
    }

重点来了,next的内容很多很多,而且我们都要弄懂。没事,我们慢慢看。首先,要看懂这些代码,我们必须知道另外三个知识点:

  • MessageQueue可以设置Barrier
  • MessageQueue可以设置IdleHandler
  • native函数

MessageQueue可以设置Barrier(栅栏),位于Barrier之后的,且没有令牌(令牌存放在成员变量mNextBarrierToken中)的,都会被拦截,而被拦截的message都无法被拿取。所以,next()返回的不一定就是队列中的首个message。MessageQueue提供了方法设置或移除Barrier。

//MessageQueue.java
public int postSyncBarrier() 
private int postSyncBarrier(long when) 
public void removeSyncBarrier(int token)

MessageQueue还可以设置IdleHandler,即空闲任务。在列表为空,或下一个message需要延时等待时,就会检测是否有IdleHandler需要执行,如果有就执行一下。相当于,利用空闲执行一些不那么紧要的任务。所有这些IdleHandler都被存在MessageQueue的成员变量mIdleHandler,这是一个IdleHandler类型的列表。IdleHandler时MessageQueue的一个内部接口,它只有一个方法。

//MessageQueue.java
public static interface IdleHandler {
        boolean queueIdle();
}

我们要使用IdleHandler十分简单

//demo2
public class MainActivity() extends Activity{
	@Override
	protected onCreate(){
    		……
   		Looper.myQueue().addIdleHandler(new MyIdleKeep());//反复执行的Idle
    		Looper.myQueue().addIdleHandler(new MyIdleOnce());//执行一次的Idle
	}
	class MyIdleKeep implements MessageQueue.IdleHandler{
       		@Override
		public boolean queueIdle(){
			//具体任务逻辑
			return true;
		}
	}
	class MyIdleOnce implements MessageQueue.IdleHandler{
		@Override
		public boolean queueIdle() {
    			//具体任务逻辑
		return false;
		}
	}
}

根据返回值的不同,将控制IdleHandler反复执行或只执行一次。当然,MessageQueue还提供了removeIdleHandler()方法。
关于native函数,这部分内容很多,我们放到后面去讲。回到我们的MessageQueue.next()方法,现在我们再来看它的逻辑就很容易理解了。首先,判断一下是否有Barrier,有就用指针标记队列里下一个有令牌的message。然后,遍历队列,看是否有需要操作的message,有的话就返回给Looper,并移除节点,一直到不再有需要操作的message为止。再然后,在队列空闲时,就执行IdleHandler列表中的任务。最后,如果啥事也没有,那就阻塞在这里。
有同学会问了,为啥会阻塞,请注意——next()内部也有个死循环,拿不到message,它就会一直循环,直到拿到为止。

Looper将message交给handler进行最终处理

现在是最后一步了,Looper得到了MessageQueue中的message后,会将它们发给对应的Handler处理。这一步就很简单了。

msg.target.dispatchMessage(msg);

其中,msg.target其实就是Handler。它们是在前面提到过的Handler.enqueueMessage()中被赋值的。

//Handler.java
private boolean enqueueMessage(MessageQueue queue, 
	Message msg, long uptimeMillis) {
        msg.target = this;
        ......
        return queue.enqueueMessage(msg, uptimeMillis);
}

所以,Looper就是调用了Handler的dispatchMessage方法。

//Handler.java
public void dispatchMessage(Message msg) {
	......
	handleMessage(msg);
}

handleMessage,这不就是我们在最开始重写的那个处理逻辑的方法吗?至此,整个message的传递流程终于完成啦。

JNI层的实现

前文我们不是提到过一个native函数吗,当时,我们限于篇幅,没有介绍。现在,我们来详细解释一下。

  • 本部分内容涉及native语言,需要一定的C和C++基础
  • 为照顾初学者,本文会稍微解释一下JNI,但最好对JNI有了解

JNI简介

网上关于JNI层的知识一抓一大把,而且这也不是今天的重点,这里简单讲几句。
为什么要用JNI?Java虽然好用且跨平台,但和C/C++比,效率就低了。为了提高效率,Android在底层采用Native语言实现。这就造成,上层是Java,底层是C/C++,那怎么沟通呢,Java怎么去调用C的函数呢?JNI帮我们解决了这个问题。至于它是如何解决的请自行百度,这里只讲怎么用。

Native函数分析

前方高能,请没有C/C++基础的读者速速退避。
Android 其实在尽全力隐藏Native层,让开发者只用关注Java代码就好,可是对于底层开发人员,我们依然会在Java层中遇见许多Native方法。我们就先打MessageQueue的源码看一看。

//MessageQueue.java
private native static long nativeInit() //返回一个指向C/C++的指针
private native static void nativeDestroy(long ptr)//destroy ptr指针对应的
private native void nativePollOnce(ptr, int timeoutMillis)//进行一次轮询
private native static void nativeWake(long ptr) //唤醒
private native static boolean nativeIsPolling(long ptr) //是否还在轮询

其实还是不少的,而且我们看到了前文跳过没讲的nativePollOnce()方法。不急,我按照时间顺序一个个讲解。

nativeInit()

nativeInit()方法在MessageQueue的构造函数中被调用了,它返回了一个long类型的值。

//MessageQueue.java
MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
}

那它的具体实现在哪里呢?答案是每一个加了native关键字的java方法,都能找到一“包名_类名_方法名”的C/C++函数与之对应。举个栗子,我们的nativeInit()方法就应该有一个android_os_MessageQueue_nativeInit的C/C++函数与之对应。

//android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {    
	NativeMessageQueue* nativeMessageQueue = 
		new NativeMessageQueue();
    	……
    	//强、弱指针计数器各加一
    	nativeMessageQueue->incStrong(env);
    	//将nativeMessageQueue强转为jlong类型
    	return reinterpret_cast<jlong>(nativeMessageQueue);
}

android_os_MessageQueue_nativeInit其实就做了两件事。
第一,调用了NativeMessageQueue的incStrong(JNIEnv* env)方法,那incStrong(JNIEnv* env)方法又做了什么呢?

//RefBase.cpp
void RefBase::incStrong(const void* id) const
{
	weakref_impl* const refs = mRefs;
	refs->incWeak(id);
	refs->addStrongRef(id);
	……
 }

incStrong()方法由MessageQueue继承自RefBase类,又继承给了NativeMessageQueue,该方法用于给NativeMessageQueue对象的强、弱指针计数器各加一。Android中将RefBase作为JNI层所有类的父类,而RefBase内维护两个计数器(强指针计数器、弱指针计数器),当两个计数器的值都为0时,表示该对象可以被回收了
设置这两个计数器的原因很简单,因为C++/C没有自动回收机制,故而为了实现Java的回收机制而设置了这些类和方法,关于RefBase其实还有很多内容,这里就不深入讨论了。读者只需知晓,这个new出来的NativeMessageQueue对象并不是函数掉完就销毁,而是要一直用,这个函数的功能就是防止NativeMessageQueue对象被回收
第二,将刚刚构造的NativeMessageQueue*对象强制转型为long类型,并将其返回Java层,作为Java操作该对象的媒介
我们可以发现,其实nativeInit()方法就是生成了个新的NativeMessageQueue对象,并返回了一个可以操纵该对象的指针,又因为Java没有指针类型,故而强转成long来保存。

nativePollOnce

操纵的索引拿到了,可以开始正式调用NativeMessageQueue类的方法了。前文已近介绍过,nativePollOnce在next()中被调用过。

//MessageQueue.java
Message next() {
	final long ptr = mPtr;
        ......
        nativePollOnce(ptr, nextPollTimeoutMillis);     //进行一次轮询
        ......
}

我们可以看到,这里传递的参数ptr就是mPtr,而mPtr又是nativeInit赋值的。nativePollOnce又干了什么呢?

//android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
	jlong ptr, jint timeoutMillis) {
    	NativeMessageQueue* nativeMessageQueue = 
    		reinterpret_cast<NativeMessageQueue*>(ptr);
    	nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

它用我们给的指针又找到了在android_os_MessageQueue_nativeInit构造的NativeMessageQueue对象,然后调用了NativeMessageQueue的pollOnce函数。继续看NativeMessageQueue的pollOnce函数。

//android_os_MessageQueue.cpp
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, 
	int timeoutMillis) {
    	mPollEnv = env;
    	mPollObj = pollObj;
	mLooper->pollOnce(timeoutMillis);
	……
}

mLooper的声明是sp类,及Looper类的强指针。所以,其实我们又调用了mLooper指向对象的pollOnce方法,即Looper::pollOnce。

//Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, 
	int* outEvents, void** outData) {
    	int result = 0;
    	for (;;) {
    		......         //一些赋值操作
        	result = pollInner(timeoutMillis);
    	}
}
int Looper::pollInner(int timeoutMillis) {
	......
	struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    	int eventCount = epoll_wait(mEpollFd, 
    		eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    	......
}

Looper的pollOnce中又调用了pollInner方法,简单的说明一下,pollInner首先根据Native Message的信息计算此次需要等待的时间。然后调用epoll_wait,等待感兴趣的事件或超时发生。当触发了事件,从epoll_wait返回一个值,返回值小于零,表示发生错误,将result设置为ALOOPER_POLL_ERROR标记;返回值为零,表示发生超时;返回值大于零,表示发生事件的个数,建立管道,处理读入的事件。最后,还要处理Native的Message和那些带回调函数的Response。
所以,我们发现,其实最后起作用的是epoll_wait,epoll是Linux内核提供的一组函数,这个说起来就又是很长了,好奇的同学请自行百度。简单的说,epoll会在looper和Linux内核之间建立管道,在looper空闲的时候,就让线程休眠并监听这个管道,这将节省系统资源,提高效率。当有相关事件触发时,就在管道的写端,写入什么东西来唤醒休眠的looper。
所以,这里的epoll_wait是会阻塞的,它的阻塞又会导致pollInner阻塞,pollInner阻塞又会导致pollOnce阻塞,进而导致Java层的pollOnce阻塞,接着时next()阻塞,然后时loop()阻塞。而loop又是工作在主线程中的,那loop()阻塞会导致主线程阻塞的话,进而使进程ANR不是完蛋了?而事实是不会的,具体的原因我们后面分析。

nativeWake

前面提到我们调用nativePollOnce时,可能会让线程休眠,既然有休眠,那就一定会有唤醒,nativeWake就是提供用来唤醒的方法,我们来看看它的具体实现。

//android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = 
    	reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}

android_os_MessageQueue_nativeWake函数在被调用时,也调用了子类NativeMessageQueue的方法NativeMessageQueue.nativeWake()。

//android_os_MessageQueue.cpp
void NativeMessageQueue::wake() {
    mLooper->wake();
}

nativeMessageQueue.nativeWake()继续向下调了mLooper.wake()。而mLooper对象前文已经提到过了,它是一个NativeMessageQueue类的指针。

//Looper.cpp
void Looper::wake() {
	……
   	uint64_t inc = 1;
    	ssize_t nWrite = TEMP_FAILURE_RETRY(
    		write(mWakeEventFd, &inc, sizeof(uint64_t)));
    	……
}

write()是系统提供的写方法,及将inc写入mWakeEventFd,写入的大小为uint64_t大小。成功则返回写入数据大小,失败则返回-1。注意,我们前面提到过,epoll会在looper和Linux内核之间建立管道,在looper空闲的时候,就让线程休眠并监听管道,如果写端有在写入就唤醒Looper。这里的write()就是在向管道写入,epoll_wait()这时就会返回结果,进而pollInner返回结果给pollOnce,然后就回到前文说的pollOnce的流程了。
这个TEMP_FAILURE_RETRY又是什么呢?TEMP_FAILURE_RETRY是个宏定义,我们调用write时,可能会失败,失败时就会返回-1,保证成功,我们就一直write()直到成功为止,TEMP_FAILURE_RETRY就是干这个一直重复的活,它的定义如下:

#define TEMP_FAILURE_RETRY(expression) \  
  (__extension__\  
   ({ long int __result;\  
       do __result = (long int)(expression);\  
       while(__result == -1L&& errno == EINTR);\  
       __result;})\  
#endif

nativeIsPolling

nativeIsPolling函数和它的几个弟兄差不多,照样是在调用NativeMessageQueue里的方法。

//android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeIsPolling(JNIEnv* env, 
	jclass clazz, jlong ptr) {
    	NativeMessageQueue* nativeMessageQueue = 
    		reinterpret_cast<NativeMessageQueue*>(ptr);
    	nativeMessageQueue-> getLooper()->isPolling();
}

NativeMessageQueue的getLooper()这个函数是从MessageQueue那继承过来的,这个函数是在android_os_MessageQueue的头文件中声明并实现的。

//android_os_MessageQueue.h
inline sp<Looper> getLooper() const {
        return mLooper;
}

又是mLooper,我就不解释了。nativeMessageQueue-> getLooper()->isPolling()其实就是调用Looper的isPolling方法。

//Looper.cpp
bool Looper::isPolling() const {
    	return mPolling;
}

isPolling返回的是Looper的成员变量mPolling,mPolling在Looper构造的时候就被赋值为false,除此之外它只在一个地方被改过。我们又回到pollInner看看,发现在调用epoll_wait之前,将mPolling 设为了true,epoll_wait结束后又将mPolling 设为了true变成了false。那我们就可以理解了,mPolling其实就是标识系统是不是在调用epoll_wait,因为我们前面提到过,epoll_wait可能会导致阻塞,进而使得Looper长时间停留在阻塞的状态,如果这时我们进行一些不当的操作可能会导致crash,故而需要用mPolling 来进行标识。

//Looper.cpp
int Looper::pollInner(int timeoutMillis) {
    	……
    	mPolling = true;

    	struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    	int eventCount = epoll_wait(mEpollFd, 
    		eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    	mPolling = false;
	……
}

JNI层MessageQueue和Looper的关系

我们可以发现一个有趣的地方——在Java层中,是Looper中维护有一个MessageQueue对象,而且该对象会由Looper的生成,随Looper的消亡而消亡。但在JNI层不一样了,我们在调用nativeInit时,会生成了一个NativeMessageQueue对象,这个对象是MessageQueue的派生类。

//android_os_MessageQueue.h
class NativeMessageQueue : public MessageQueue, public LooperCallback {};

NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    	mLooper = Looper::getForThread();
   	if (mLooper == NULL) {
        	mLooper = new Looper(false);
        	Looper::setForThread(mLooper);
    	}
}

我们看到在JNI层Looper是由NativeMessageQueue对象创建的,而且NativeMessageQueue的很多函数都是调用Looper的函数来实现的。所以,Java层到JNI层的入口是在MessageQueue这里,具体的说就是nativeInit()方法。

Looper不会导致主线程crash

我们在将nativePollOnce时提到过,nativePollOnce有时是会阻塞的,而它的阻塞最终会反应到线程中,那为什么实际操作中主线程没有crash呢?

Activity的入口ActivityThread

Activity的创建流程我们就不详细叙述了,我们需要知道的是,当ActivityThread的main()方法被调用,那么主线程就跑起来了。它都干了什么呢?它先进行了一堆配置和初始化,然后调用了Looper.loop(),当loop()结束,就报错。

public static void main(String[] args) {
        ......
        Looper.prepareMainLooper();
	......
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
	......
	//以上都是初始化和配置
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

肯定有读者表示,惊了,这是什么鬼。主要不理解的可能集中在三个地方:

  • 为啥要用loop()
  • loop()不会让主线程anr吗
  • 主线程是如何处理子线程中传来的请求的

首先,我们要想到,主线程的生命长度应该是多久呢?应当是从应用被启动直到应用被杀死对不对。我们知道机器会逐条执行我们的代码,如果没有特殊处理,那哪怕我们写了一万条代码,也总有执行完的时候,到时候线程就会被杀死。这显然不符合我们的要求,所以我们一定要造个无限循环出来,让主线程的任务永远执行不完,也就不会被杀死,而Looper帮我们解决了这个问题。这也是为什么,当 Looper.loop退出后,会抛出异常,因为主线程马上就要执行完了,要被杀了,系统当然认为出了问题。
然后,为什么不会让主线程anr呢。anr的直接原因是,进行某项耗时任务,导致在一定时间内无法响应用户的交互。举个栗子,从数据库读取图片,结果读了半天也没出来,导致UI界面也刷新不了,于是ANR。而对主线程来说,当主线程的初始化和配置完了后,就一直在looper里循环,这时,主线程已经完成了它所有的任务,肯定不会有什么问题。当有任务被传递给主线程时,Looper就会将它分发给Handler来处理。很多人认为Looper会导致ANR是觉得Looper会是这种结构:

public void main(){
	......
	Looper.loop();
	任务一;
	任务二;
	任务三;
	......
}

可其实Looper是这种结构:

public void main(){
	......
	Looper.loop();
}
public void loop(){
	for(i为0到无穷){
		任务i;
	}
}

这种情况下,一旦出现了ANR,绝对不是由loop引起的,而绝对是具体处理逻辑的问题。
最后,我们好像没有看到主线程的处理逻辑,那当它接到子线程的任务时是怎么反应的呢?我们前面说过,Looper负责分发,而Handler负责处理。那我们来看看主线程的Handler

public void handleMessage(Message msg) {
            switch (msg.what) {
                case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case EXIT_APPLICATION:
                    if (mInitialApplication != null) {
                        mInitialApplication.onTerminate();
                    }
                    Looper.myLooper().quit();
                    break;
                case RECEIVER:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
                    handleReceiver((ReceiverData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case CREATE_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case BIND_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                    handleBindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                    ......
            }
        }
    }

这里只晒出了一部分行为,但处理逻辑已经很清楚了,当有任务被分发给主线程时,会被存放到主线程创建的Looper的MessageQueue中,然后被Looper检测到,被检测到的message就会被发送到handler中进行具体处理。
到此为止,Handle机制的实现原理基本就讲述清楚了,但其实还有很多细节可以挖,但是限于篇幅这里就不细说了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值