Android是消息驱动的,实现消息驱动有几个要素:
1、Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理
2、处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message
msg)方法来对特定的Message进行处理,例如更新UI等。
3、MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
4、Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
5、Thread:线程,负责调度整个消息循环,即消息循环的执行场所。获取loop
Looper.loop();
Public staticvoidloop(){
finalLooperme=myLooper();
}
//返回和线程相关的looper
Public staticLoopermyLooper(){
returnsThreadLocal.get();
}
Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列和一个消息循环(Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作线程默认是没有消息循环和消息队列的,如果想让该
线程具有消息队列和消息循环,需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。 如下例所示:
class LooperThread
extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Messagemsg) {
// process incoming messageshere
}
};
Looper.loop();
}
}
这样你的线程就具有了消息处理机制了,在Handler中进行消息处理。
Activity是一个UI线程,运行于主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper)。详细实现请参考ActivityThread.java文件
Android应用程序进程在启动的时候,会在进程中加载ActivityThread类,并且执行这个类的main函数,应用程序的消息循环过程就是在这个main函数里面实现的
public final class
ActivityThread {
......
public
static final void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread
thread = new ActivityThread();
thread.attach(false);
......
Looper.loop();
......
thread.detach();
......
}
}
这个函数做了两件事情,一是在主线程中创建了一个ActivityThread实例,二是通过Looper类使主线程进入消息循环中
初始化消息队列
class LooperThread
extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Messagemsg) {
// process incoming messageshere
}
};
Looper.loop();
}
}
主要是红色标明的两句,首先调用prepare初始化MessageQueue与Looper,然后调用loop进入消息循环。先看一下Looper.prepare。
public static void
prepare() {
prepare(true);
}
private static void
prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Onlyone Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
重载函数,quitAllowed默认为true,从名字可以看出来就是消息循环是否可以退出,默认是可退出的,Main线程(UI线程)初始化消息循环时会调用prepareMainLooper,传进去的是false。使用了ThreadLocal,每个线程可以初始化一个Looper。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();//mPtr记录native消息队列的信息
}
在Looper初始化时,新建了一个MessageQueue的对象保存了在成员mQueue中。MessageQueue的构造函数是包可见性,所以我们是无法直接使用的,在MessageQueue初始化的时候调用了nativeInit,这是一个Native方法:
android_os_MessageQueue_nativeInit();
static jlong
android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue*nativeMessageQueue = new NativeMessageQueue();//初始化native消息队列
if (!nativeMessageQueue) {
jniThrowRuntimeException(env,"Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
returnreinterpret_cast(nativeMessageQueue);
}
在nativeInit中,new了一个Native层的MessageQueue的对象,并将其地址保存在了Java层MessageQueue的成员mPtr中,Android中有好多这样的实现,一个类在Java层与Native层都有实现,通过JNI的GetFieldID与SetIntField把Native层的类的实例地址保存到Java层类的实例的mPtr成员中,比如Parcel。
再看NativeMessageQueue的实现:
new
NativeMessageQueue();
NativeMessageQueue::NativeMessageQueue()
: mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper =Looper::getForThread(); //获取TLS中的Looper对象
if (mLooper == NULL) {
mLooper = newLooper(false); //创建native层的Looper
Looper::setForThread(mLooper); //保存native层的Looper到TLS中
}
}
Looper::getForThread(),功能类比于Java层的Looper.myLooper();
Looper::setForThread(mLooper),功能类比于Java层的ThreadLocal.set();
NativeMessageQueue::NativeMessageQueue()
: mInCallback(false), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
在NativeMessageQueue的构造函数中获得了一个Native层的Looper对象,Native层的Looper也使用了线程本地存储
发送消息
通过Looper.prepare初始化好消息队列后就可以调用Looper.loop进入消息循环了,然后我们就可以向消息队列发送消息,消息循环就会取出消息进行处理,在看消息处理之前,先看一下消息是怎么被添加到消息队列的。
在Java层,Message类表示一个消息对象,要发送消息首先就要先获得一个消息对象,Message类的构造函数是public的,但是不建议直接new Message,Message内部保存了一个缓存的消息池,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message,每次使用完后系统放入缓存池,会占用很多内存的,如下所示:
public static
Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
public void recycle() {
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
Message内部通过next成员实现了一个链表,这样sPool就了为了一个Messages的缓存链表。
消息对象获取到了怎么发送呢,大家都知道是通过Handler的post、sendMessage等方法,其实这些方法最终都是调用的同一个方法sendMessageAtTime:
public boolean
sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = newRuntimeException(
this + "sendMessageAtTime() called with no mQueue");
Log.w("Looper",e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg,uptimeMillis);
}
sendMessageAtTime获取到消息队列然后调用enqueueMessage方法,消息队列mQueue是从与Handler关联的Looper获得的。
private boolean
enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg,uptimeMillis);
}
enqueueMessage
接下来看一下是怎么MessageQueue的enqueueMessage。
final boolean
enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw newAndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw newAndroidRuntimeException("Message must have a target.");
}
boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = newRuntimeException(
msg.target + "sending message to a Handler on a dead thread");
Log.w("MessageQueue",e.getMessage(), e);
return false;
}
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when< p.when) {
// New head, wake up the eventqueue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middleof the queue.Usually we don't have towake
// up the event queue unlessthere is a barrier at the head of the queue
// and the message is theearliest asynchronous message in the queue.
needWake = mBlocked &&p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when
break;
}
if (needWake &&p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p== prev.next
prev.next = msg;
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
在enqueueMessage中首先判断,如果当前的消息队列为空,或者新添加的消息的执行时间when是0,或者新添加的消息的执行时间比消息队列头的消息的执行时间还早,就把消息添加到消息队列头(消息队列按时间排序),否则就要找到合适的位置将当前消息添加到消息队列。
消息循环
消息队列初始化好了,也知道怎么发消息了,下面就是怎么处理消息了,看Handler.loop函数:
public static void
loop() {
final Looper me =myLooper();//从该线程中取出对应的looper对象
if (me == null) {
throw new RuntimeException("NoLooper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//取消息队列对象...
// Make sure the identity of thisthread is that of the local process,
// and keep track of what that identitytoken actually is.
Binder.clearCallingIdentity();
final long ident =Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next();// might block取消息队列中的一个待处理消息..
if (msg == null) {
// No message indicates thatthe message queue is quitting.
return;
}
// This must be in a localvariable, 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 courseof dispatching the
// identity of the thread wasn'tcorrupted.
final long newIdent =Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Threadidentity 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();
}
}
loop每次从MessageQueue取出一个Message,调用msg.target.dispatchMessage(msg),target就是发送message时跟message关联的handler,这样就调用到了熟悉的dispatchMessage,Message被处理后会被recycle。当queue.next返回null时会退出消息循环,接下来就看一下MessageQueue.next是怎么取出消息的,又会在什么时候返回null。
final Message next()
{
int pendingIdleHandlerCount = -1; // -1only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr,nextPollTimeoutMillis);
synchronized (this) {
if (mQuiting) {
return null;
}
// Try to retrieve the nextmessage.Return if found.
final long now =SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null &&msg.target == null) {
// Stalled by abarrier.Find the next asynchronousmessage in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null&& !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is notready.Set a timeout to wake up when itis 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);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// If first time idle, then getthe number of idlers to run.
// Idle handles only run if thequeue is empty or if the first message
// in the queue (possibly abarrier) 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 torun.Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers ==null) {
mPendingIdleHandlers = newIdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers =mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this codeblock during the first iteration.
for (int i = 0; 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 threwexception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, anew message could have been delivered
// so go back and look again for apending message without waiting.
nextPollTimeoutMillis = 0;
}
}
MessageQueue.next首先会调用nativePollOnce,然后如果mQuiting为true就返回null,Looper就会退出消息循环。