从源码的角度解析android的消息机制,结合7.0源码,重新梳理一下android的消息机制。 Message ,Looper,Handler,MessageQueue的关系。
我们都知道Android的UI不是线程安全的,所以不能在子线程中操作UI组件,如果子线程中有操作UI的需求。我们都会通过handler来进行线程间的通信。
例如:
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//操作ui
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
//构建Message对象
Message message=Message.obtain();
//发送消息
handler.sendMessage(message);
}
}).start();
}
例如上面的代码,在子线程中,构建一个message对象,然后通过handler发送这个message对象。然后我们就可以在handler的handleMessage方法中接收这个消息,然后操作ui组件。
这个代码我们写的太多了,闭着眼睛也可以写了。但是如果要深究原理的话,就会有很多疑问。
比如:
1.为什么handleMessage里方法就可以操作ui组件呢(ps:在android中,我们把响应用户操作的线程称为主线程,也叫ui线程。线程名为:”main”,既然handleMessage方法中能够操作ui组件,那说明他执行的线程就是在main线程中的,但为什么它执行在main线程中的呢)?
2.为什么handler.sendMessage方法后,为什么会回调handleMessage呢?他们之间的调用栈是怎样的?
第一个问题,有点大,我们先来看第二个问题! 跟进sendMessage的源码,发现除了postAtFrontOfQueue这个方法外,所有的handler的sendxx,postxx方法,最后都会回调sendMessageAtTime方法。代码如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
那个方法没有调用sendMessageAtTime方法,但是在里面调用了sendMessageAtFrontOfQueue方法。这个方法的实现和sendMessageAtTime完全相同。然后我们看sendMessageAtTime这个方法的实现,可以看到先给MessageQueue 对象赋值,然后调用了enqueueMessage方法,代码如下。 最后调用了queue对象的enqueueMessage方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
我们先整理一下MessageQueue对象的值是从哪来来的,然后再看enqueueMessage方法的实现。
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到我们mQueue是从mLooper中取出来的。 那这个Looper又是干什么的呢? 带着疑问,我继续看一下MessageQueue的enqueueMessage方法。
......
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;
}
......
}
enqueueMessage的方法代码量有点多,具体的逻辑看不太懂。 但是大概的意思是判断了一通,然后把message对象插入到链表中。代码分析到这里就有点蒙了 。 what… 我们从handler.sendxx方法,沿着调用栈,分析了一通,发现handler.sendxx方法就是把message插入到Looper对象的MessageQueue链表中。 那handleMessage方法啥时候调用呢? 不说清楚别想走 .哈哈。 别急,这个时候我们就需要回头想想,平时我们在主线程给子线程中发消息,需要构建子线程handler对象,然后还需要做什么呢? 对了。我们需要构建Looper然后,调用Looper的Loop方法。 例如下面这样:
public class MainActivity extends Activity {
Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//向子线程发送
Message message=Message.obtain();
handler.sendMessage(message);
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//执行在子线程
}
};
Looper.loop();
}
}).start();
}
}
我们想想,同样是handler发送消息,为什么子线程中需要调用Looper.prepare(), Looper.loop()呢,而主线不需要,还有就是发送消息和接收消息,为什么要调用Looper.prepare()和 Looper.loop()方法呢? 想清楚这两个问题,我们之前的疑惑就自然而然的解开了。
我们先看一下Looper.prepare()方法:
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));
}
可以看到在prepare方法中,构建了一个Looper对象,然后放入到ThreadLocal对象中。看到这里我不禁想起之前看到的handle构造方法里面的那段代码:
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
原来这个looper对象是用来构建handler的时候使用的呀。然后跟进去myLooper方法,
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到Looper对象是从ThreadLocal中取出的。 这里得说明一下ThreadLocal类的作用,这个类日常开发的时候很少使用。但是在一些场景用起来却是很适合。这个类可以保证每个线程都保存一份变量,线程与线程间的数据相互隔离。比如,我们在哪个线程中get出来的对象,就是我们之前在这个进程set进去的对象。
然后看一下Looper.myLooper()方法,
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
......
for (;;) {
Message msg = queue.next(); // might block
......
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
}
}
Looper.myLooper()方法中代码比较多,我们只看关键代码,可以看到先是取出当前线程的looper对象,然后用一个无限循环从Looper中的queue中读取消息(queue.next()),然后传递给msg.target的dispatchMessage方法。这里msg的tag就是我们的handler对象。 分析到这里,我们就梳理清楚handler消息机制了,我们总结一下:先是handler发送消息,插入到当前线程Looper中MessageQueue链表中,然后在Looper的loop方法中无限循环从MessageQueue读取消息,传递给handler.dispatchMessage方法! 然后我们看一下dispatchMessage方法,代码如下:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到我们处理消息有3个顺序,首先是msg.callback,mCallback.handleMessage,handleMessage,跟上源码看一下赋值过程:首先是msg.callback的赋值过程,代码如下:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
这个callback就是我们在handler.post的时候赋值的runable对象。然后回到dispatchMessage方法,如果
msg.callback不为空执行就执行handleCallback,代码如下:
private static void handleCallback(Message message) {
message.callback.run();
}
所以我们知道handler的post方法也是执行在主线程中的。而且post处理消息的优秀级最高。
然后我们看一下mCallback,这个mCallback就是我们下面这种方式创建的handler的时候的Handler.Callback对象:
Handler handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
Handler.Callback处理消息的优先级也比handler.post低,但是高于handleMessage方法。
现在我们分析清楚了handler的消息机制,以及message是怎么传递和被处理的。然后我们回头看看主线程中的消息是怎么被处理的,它也应该和子线程一样使用handler一样,需要构建一个looper,然后调用looper的loop方法。代码在哪里呢,我们找一下:
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
原来ActivityThread的main方法中。我们知道,当我们应用程序启动的时候,首先Zygote fork出一个新的进程,然后会调用ActivityThread的main方法。 原来我们在主线程中使用handler的时候系统已经默认帮我们做了Looper的初始化工作。
分析到这里我们已经知道,handler发送消息之后是怎么传递的,会回调handler的哪个方法,子线程和主线程的looper初始化工作是怎么实现的。 但是还是有一个最大的疑问,为什么消息传递到handler的dispatchMessage方法中后,我们就发现如果是主线程的handler,dispatchMessage方法就执行在主线程,如果是子线程的handler,dispatchMessage就执行在子线程? 换句话说为什么是 dispatchMessage方法是执行在Looper所在的线程? >- -< 回答这个问题,因为dispatchMessage方法是在Looper.loop方法中回调的,所以当前执行在loop方法所在的线程。loop方法执行在Looper所在的线程。
然后我们先整理一下其他的几个特殊的方法:
Activity的runOnUiThread方法:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
可以看到如果不是在主线程,就调用handler的post方法(这里的这个mHandler对象是Activity的,构造在主线程中),如果是在主线程就直接执行。
View的post方法:
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
查看源码可知,这里的attachInfo!=null表示View已经添加到Window中,就执行主线程的handler.post方法(attachInfo.handler是在Activity的handler对象)。
如果View没有attach到window中,那就会在ViewRootImpl下一次performTraversals中执行。 这种情况比较复杂,可以看一下文章后面的参考链接中讲解View.post的文章。
总结几点:
- 1.handler的.dispatchMessage方法是执行在Looper所在的线程的。
- 2.每个线程的Looper只有一个,也就是说每个线程的MessageQueue只有一个,无论这个线程中有多少个handler。 ps:现在很多的性能监控组件会监控app的Ui卡顿情况,很多的实现思路就是,定时的向主线程的Looper发送消息,判断消息处理的时间间隔是否达到一定的时间。 从而判断ui线程是否卡顿。
参考链接:
http://blog.csdn.net/a740169405/article/details/69668957