Android Handler中的handleMessage方法和post方法之源码剖析 及UI更新方法
2016-08-05 15:18
471 查看
我们都知道,在子线程中进行UI操作(更新UI控件)包括以下四种方法:
1.Handler的handlerMessage()方法。
2.Handler的post()方法。
3.View的post()方法。
4.Activity的runOnUiThread()方法。
本文重点分析前两种方法,后面两种稍微说一下。在说第一个方法之前,让我们先来看张图片(图片来源于http://my.oschina.net/keeponmoving/blog/61129)
这个图片在很好的说明了Handler中的handleMessage方法的工作原理(至少说明了怎么回事)。但是这个图有个问题,作者把handleMessage画到了主线程的外面,其实应该是要在主线程里面,这样才能通过handleMessage来操作主线程的ui,接下来详细说明原因:
先看看如何创建handler吧,其构造函数如下:
[java]
view plain
copy
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> 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()这个方法:
[java]
view plain
copy
public static finalLooper myLooper() {
return (Looper)sThreadLocal.get();
}
这个方法功能很简单,就是从sThreadLocal线程中取出Looper对象。如果sThreadLocal线程有Looper对象则返回,如果没有则返回空。那什么时候给sThreadLocal线程设定的Looper对象呢?来看看Looper.prepare()方法吧:
[java]
view plain
copy
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()方法源码:
[java]
view plain
copy
public static void main(String[] args) {
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(newEventLoggingReporter());
Process.setArgV0("<pre-initialized>");
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():
[java]
view plain
copy
Message message = newMessage();
mHandler.sendMessage(message);
从上面的代码可以看出,每次sendMessage的时候都需要先new一个新的message,比较浪费内存空间。那还有没有更好的办法呢?再来看一下obtainMessage():
[java]
view plain
copy
public final Message obtainMessage(int what, int arg1, int arg2, Object obj){
return Message.obtain(this, what, arg1,arg2, obj);
}
这个方法调用了Message的静态方法obtain,来一起看下obtain()方法的源代码:
[java]
view plain
copy
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()方法,源代码如下:
[java]
view plain
copy
/*
* 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()方法中,其源代码如下所示:
[java]
view plain
copy
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()就是入队的方法了,源代码如下:
[java]
view plain
copy
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 <p.when) {
msg.next = p;
mMessages = msg;
this.notify();
} else {
Message prev = null;
while (p != null && p.when<= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
this.notify();
}
}
return true;
}
通过这个方法名可以看出来通过Handler发送消息实质就是把消息Message添加到MessageQueue消息队列中的过程而已。通过上面遍历等next操作可以看出来,MessageQueue消息队列对于消息排队是通过类似c语言的链表来存储这些有序的消息的。其中的mMessages对象表示当前待处理的消息,然后18到49行可以看出,消息插入队列的实质就是将所有的消息按时间进行排序。既然有入队,那么肯定会有出队的啊,那么出队又是什么鬼样呢?
[java]
view plain
copy
/**
* 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()方法就是消息队列的出队方法。如下所示:
[java]
view plain
copy
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()方法,如下:
[java]
view plain
copy
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方法中了。所以,一个标准的异步消息处理的函数应该就是这样子的:
[java]
view plain
copy
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参数:
[java]
view plain
copy
handler.post(new Runnable() {
@Override
public void run() {
//do something
}
});
我们来看下post的源码吧:
[java]
view plain
copy
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
返回一个sendMessageDelayed方法,有没有觉得很熟悉?至少sendMessageAtTime(上面已经分析过这个源码了)很熟吧,其实sendMessageDelayed就是调用的sendMessageAtTime方法,源代码如下:
[java]
view plain
copy
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对象,如下所示:
[java]
view plain
copy
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()方法中的代码吧:
[java]
view plain
copy
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方法:
[java]
view plain
copy
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()方法,如下所示:
[java]
view plain
copy
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
1.Handler的handlerMessage()方法。
2.Handler的post()方法。
3.View的post()方法。
4.Activity的runOnUiThread()方法。
本文重点分析前两种方法,后面两种稍微说一下。在说第一个方法之前,让我们先来看张图片(图片来源于http://my.oschina.net/keeponmoving/blog/61129)
这个图片在很好的说明了Handler中的handleMessage方法的工作原理(至少说明了怎么回事)。但是这个图有个问题,作者把handleMessage画到了主线程的外面,其实应该是要在主线程里面,这样才能通过handleMessage来操作主线程的ui,接下来详细说明原因:
先看看如何创建handler吧,其构造函数如下:
[java]
view plain
copy
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> 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()这个方法:
[java]
view plain
copy
public static finalLooper myLooper() {
return (Looper)sThreadLocal.get();
}
这个方法功能很简单,就是从sThreadLocal线程中取出Looper对象。如果sThreadLocal线程有Looper对象则返回,如果没有则返回空。那什么时候给sThreadLocal线程设定的Looper对象呢?来看看Looper.prepare()方法吧:
[java]
view plain
copy
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()方法源码:
[java]
view plain
copy
public static void main(String[] args) {
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(newEventLoggingReporter());
Process.setArgV0("<pre-initialized>");
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():
[java]
view plain
copy
Message message = newMessage();
mHandler.sendMessage(message);
从上面的代码可以看出,每次sendMessage的时候都需要先new一个新的message,比较浪费内存空间。那还有没有更好的办法呢?再来看一下obtainMessage():
[java]
view plain
copy
public final Message obtainMessage(int what, int arg1, int arg2, Object obj){
return Message.obtain(this, what, arg1,arg2, obj);
}
这个方法调用了Message的静态方法obtain,来一起看下obtain()方法的源代码:
[java]
view plain
copy
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()方法,源代码如下:
[java]
view plain
copy
/*
* 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()方法中,其源代码如下所示:
[java]
view plain
copy
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()就是入队的方法了,源代码如下:
[java]
view plain
copy
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 <p.when) {
msg.next = p;
mMessages = msg;
this.notify();
} else {
Message prev = null;
while (p != null && p.when<= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
this.notify();
}
}
return true;
}
通过这个方法名可以看出来通过Handler发送消息实质就是把消息Message添加到MessageQueue消息队列中的过程而已。通过上面遍历等next操作可以看出来,MessageQueue消息队列对于消息排队是通过类似c语言的链表来存储这些有序的消息的。其中的mMessages对象表示当前待处理的消息,然后18到49行可以看出,消息插入队列的实质就是将所有的消息按时间进行排序。既然有入队,那么肯定会有出队的啊,那么出队又是什么鬼样呢?
[java]
view plain
copy
/**
* 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()方法就是消息队列的出队方法。如下所示:
[java]
view plain
copy
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()方法,如下:
[java]
view plain
copy
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方法中了。所以,一个标准的异步消息处理的函数应该就是这样子的:
[java]
view plain
copy
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参数:
[java]
view plain
copy
handler.post(new Runnable() {
@Override
public void run() {
//do something
}
});
我们来看下post的源码吧:
[java]
view plain
copy
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
返回一个sendMessageDelayed方法,有没有觉得很熟悉?至少sendMessageAtTime(上面已经分析过这个源码了)很熟吧,其实sendMessageDelayed就是调用的sendMessageAtTime方法,源代码如下:
[java]
view plain
copy
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对象,如下所示:
[java]
view plain
copy
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()方法中的代码吧:
[java]
view plain
copy
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方法:
[java]
view plain
copy
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()方法,如下所示:
[java]
view plain
copy
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
相关文章推荐
- Android Handler中的handleMessage方法和post方法之源码剖析
- Android异步更新UI的方式之使用Handler的post(Runnabel r)方法
- Android开发之通过Handler的post方法更新UI
- Android 更新UI的两种方法——handler和runOnUiThread()
- Android 更新UI的两种方法——handler和runOnUiThread()
- Android 更新UI的两种方法——handler和runOnUiThread()
- Android 更新UI的两种方法——handler和runOnUiThread(
- Android:UI更新方法二:View.postInvalidate+Thread+Runnable
- Android 更新UI的两种方法——handler和runOnUiThread()
- Android 更新UI的两种方法——handler和runOnUiThread()
- Android更新UI的两种方法——handler与runOnUiThread()
- Android 更新UI的两种方法——handler和runOnUiThread()
- Android 更新UI的两种方法——handler和runOnUiThread()
- Android 更新UI的两种方法——handler和runOnUiThread()
- Android 更新UI的两种方法——handler和runOnUiThread()
- 利用Handler更新Android UI的另一种方法
- Android 更新UI的两种方法——handler和runOnUiThread()
- Android 更新UI的两种方法——handler和runOnUiThread()
- android线程控制UI更新(Handler 、post()、postDelayed()、postAtTime)
- Android更新UI的两种方法——handler与runOnUiThread()