您的位置:首页 > 移动开发 > Android开发

Android的消息机制(源码篇)

2017-12-27 23:47 134 查看

大致流程

将当前线程转成Looper线程;Handler发送消息,插到MessageQueue(消息队列);looper进入循环,如果在消息队列中拿到Message就交给Handler,如果消息队列中没有消息则阻塞。

线程的分类及通信

我们都比较明白线程可分为主线程和工作线程,还有线程之间的通信分为主线程与工作线程、工作线程与工作线程(比较少听到)。

相关的对象

上一篇基本都有提到,在这继续说一下

Message:线程间通信的信息,是消息的载体;

MessageQueue:消息队列,用于存放Handler发送的消息,里面是一个链表结构,每个线程只有一个MessageQueue对象

Looper:每个线程通过发送Message保存在MessageQueue中,Looper用于去消息队列中拿取消息;有两个重要的方法:prepare()方法,创建Looper对象并将其设置到ThreadLocal中; loop()方法,产生无限循环,每当发现一个消息,就从队列中拿取出来,最后传到Handler的handleMessage方法中。

Handler:用于发送消息和处理消息;sendMessage方法、sendXXX等方法,最终是调用sendMessageAtTime方法;除了sendMessageAtFrontOfQueue方法;只要在Looper线程中构建Handler,那么这个Handler实例就会获取该Looper线程中的MessageQueue实例的引用,此后调用sendMessage方法就会通过此引用往消息队列插入消息。

附上自己画的一张图,有点丑,但可以将就将就



Handler的创建

1.在主线程中创建

Handler handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;

}
});


2.在工作线程中创建

new Thread(new Runnable() {
@Override
public void run() {
//创建Looper
Looper.prepare();
Handler handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;

}
});
//开始循环
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");
}
//将Looper对象设置到ThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}


其中的ThreadLocal在上一篇有讲过用法;主要是在不同线程中设置不同的值。

Looper的构造方法

private Looper(boolean quitAllowed) {
//初始化消息队列和指定为当前线程
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}


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) {
//队列没有消息则跳出
return;
}

// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}

final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//msg.target为Handler,此方法为将消息分发
msg.target.dispatchMessage(msg);
} finally {
……
msg.recycleUnchecked();
}
}


这个方法比较长,省略了其中一小部分的代码。

它的作用主要是进入死循环,从消息队列获取消息,如果消息队列没有消息则会停止循环。如果有找到就进行消息的分发

dispatchMessage方法

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {//这里就回调了handleMessage方法
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//查看源码是一个空实现
}
}


进入此方法可知道如果创建Handler时有传进去Callback,则Callback回调了handleMessage方法。否则最后会回调Handler本身的handleMessage方法。

sendMessage方法:跟踪到最后是调用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);
}


enqueueMessage方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//这里就是将消息加入消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}


消息队列中的enqueueMessage方法

boolean enqueueMessage(Message msg, long when) {
……
//同步,因为可能同时有多个消息加入(在不同线程中)
synchronized (this) {
……
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 {
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;
}
if (needWake) {//唤醒线程(Looper循环)
nativeWake(mPtr);//此方法是一个native的方法
}
}
return true;
}


从上面代码可知,往消息队列插入消息时,会分两种情况去插入消息,如果消息队列本身是空的,在最后则会唤醒主线程去处理消息

Handler的创建:

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;
}


这里顺便跟踪一下myLooper方法

public static @Nullable Looper myLooper() {
//从ThreadLocal中拿到Looper对象
return sThreadLocal.get();
}


总结

通过以上源码的分析,我们可以得出两点

可以知道为什么在创建Handler前要执行Looper.prepare方法。(因为没有先执行Looper.prepare()会抛异常)

Handler一创建便持有本线程的消息队列和Looper对象,所以它可以在其他线程中发送消息,最后还是会回到创建时的线程(调用HandleMessage方法)。

疑惑

不知你也会跟我一样又产生一个疑惑,在主线程中创建Handler对象不需要调用Looper.prepare和Looper.loop。

查看了文章才大概明白了是为什么。在Activity的创建过程,走到ActivityThread中的main方法

public static void main(String[] args) {
……
Looper.prepareMainLooper();//!!!
ActivityThread thread = new ActivityThread();
thread.attach(false);
……
Looper.loop();//!!!
throw new RuntimeException("Main thread loop unexpectedly exited");
}


原来是在主线程中系统会执行:Looper.prepareMainLooper()和Looper.loop();

到此消息机制的源码分析也差不多啦,可能被我拆得比较碎,但希望我们都可以从中学习到东西。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 消息机制