学习消息机制之前,我们先来了解一下消息机制,为什么要使用消息机制?采用消息机制有以下好处:
第一,android 子线程不能刷新界面,因为很多耗时操作,你必须子线程来做否则会严重影响用户体验。所以必须要用handler 来给主线程发消息,简单的说消息机制就是简化了线程之间的通信。
第二,减少功能的重复,因为很多功能我们可以调用系统来做,消息的好处是让系统多承担一些工作。
第三,采用消息机制使用具体异构性,使用activity之间可以传递更多的消息。
总之,采用消息机制的好处还是很多的。下面我们再看一下下面几个问题:
1、消息的组成:消息有什么格式,是否可以自定义消息来发送通知和传送数据?
2、谁将收到消息?
3、谁能发送消息?
4、未处理的消息到那里去了?
我们先看一下具体的类再分析上面的问题的
Message:消息,理解为线程间通讯的数据单元,消息的构成类。
Message Queue:消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
Handler:Handler是Message的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。
Looper:循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。
消息机制采用的是谁发送,谁处理的原则。Handler是消息的发送者,也是消息的处理者。我们可以重写Handler的dispatchMessage方法来进行消息的过滤,
重写handleMessage方法来进行消息的处理。我们可以用sendMessage来发送消息,我们先来看一下消息Message类:
public final class Message implements Parcelable 实现的Parcelable接口,证明它是可以跨进程传输的。Message类里面有很多变量,what主要用记标识msg的类型,
obj是指msg传输的对象。这里面还有一个重要的变量:target它代表消息的发送者,也就是Handler对象。
为了更好的理解消息去哪里了,我们来看一下sendMessage方法:
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);
}
uptimeMillis代表消息发送是否延迟,第一获取当前的消息队列,MessageQueue,然后调用 enqueueMessage方法把消息发送出去,再看一下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
msg.target = this;标识发送送本身,然后调用MessageQueue方法enqueueMessage进行发送:我们再看消息队列的发送方法。
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("Main thread not allowed to quit");
}
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
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;
}
如果消息队列里面没有任何消息,我们先创建头:
if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; }
如果已经有其他消息,我们就添加到尾部:
msg.next = p; // invariant: p == prev.next prev.next = msg;这里最简单的链表操作,next指向下一条消息。到这里消息发送的整个过程已经讲完,是不是很简单。我们再回去到sendMessageAtTime方法里面看一下: MessageQueue queue = mQueue这里面的成员变量哪里来的?
public Handler(Callback callback, boolean async) {
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 following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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是由Looper提供的,我们再看一下Looper里面的消息队列,
每个线程都可以并仅可以拥有一个Looper实例,消息队列MessageQueue在Looper的构造函数中被创建并且作为成员变量被保存,也就是说MessageQueue相对于线程也是唯一的,Looper对象有一个ThreadLocal<Looper> 变里来保证每个线程都只有一个looper对象。
到此我们已经明白了当初提的四个问题:
1.消息组成就是Message。
2.消息由Handler发送,
3.消息也是由Handler处理。
4.未处理的消息会被添加到MessageQueue里面。
但我们还是有几个问题没有明白:
1.Looper创建了消息队列,那谁创建了Looper?
2.Looper里面添加了消息,这些消息什么时候被处理?
我们先来搞清楚第一个问题,谁创建了Looper ? Looper是一个进程只有一个,我们要更新界面必须在主线程里面更新,那会不会Looper是在主线程里面创建的?我们看一下主线程的创建,直接看activityThread里面的main方法:
Looper.prepareMainLooper();
·······
·······
Looper.loop();
看一下:prepareMainLooper方法做了什么?
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
prepare方法主要创建Looper对象,同时初始化消息队列,接着再看一下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.recycle();
}
}
无限for循环里面取出MessageQueue里面的消息,也就是启动一个
循环不断的遍历消息队列,有消息添加进来就会调用Handler去处理。可能你会觉的很消耗资源,毕竟在主线程里面有这样一个无限循环,我们再来看一下消息队列的next方法:final Message next() {
boolean tryIdle = true;
while (true) {
long now;
Object[] idlers = null;
// Try to retrieve the next message, returning if found.
synchronized (this) {
now = SystemClock.uptimeMillis();
Message msg = pullNextLocked(now);
if (msg != null) return msg;
if (tryIdle && mIdleHandlers.size() > 0) {
idlers = mIdleHandlers.toArray();
}
}
// There was no message so we are going to wait... but first,
// if there are any idle handlers let them know.
boolean didIdle = false;
if (idlers != null) {
for (Object idler : idlers) {
boolean keep = false;
try {
didIdle = true;
keep = ((IdleHandler)idler).queueIdle();
} catch (Throwable t) {
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
}
// While calling an idle handler, a new message could have been
// delivered... so go back and look again for a pending message.
if (didIdle) {
tryIdle = false;
continue;
}
synchronized (this) {
// No messages, nobody to tell about it... time to wait!
try {
if (mMessages != null) {
if (mMessages.when-now > 0) {
Binder.flushPendingCommands();
this.wait(mMessages.when-now);
}
} else {
Binder.flushPendingCommands();
this.wait();
}
}
catch (InterruptedException e) {
}
}
}
}
这里有一个wait方法,我们可以看到消息队列里面如果有消息就会取出消息执行,如果没有就会进入阻塞状态,那么什么时候会唤醒呢?应该是在添加消息的时候会唤醒,我再回过头来看一下添加消息方法;里面有一个notify方法。
至此,我们已经回答上面的两个问题:第一,Looper是在主线程中创建的,也就是ActivityThread里同创建,并且一创建就进入消息循环。如果消息队列里面没有消息就进入阻塞状态。
第二,消息添加到消息队列里面马上会唤醒主线程来对消息进行处理。
我们先来总结一下消息机制:
第一,android 在进程启动的时候,创建了Looper 和MessageQueue,同时looper进入循环检测消息队列有没有消息,
第二。创建handler 用于发消息Message,也生写handleMessage对消息进行处理。
也许到这里大家可能都没有问题了,但我还是有一个问题:多个handler可以共用一个MessageQueue吗?
我们一起来解答这个问题,答案是:肯定可以的,那个MessageQueue处理消息的时候如何查找相应的handler呢?
还是在源码中寻找答案,我们都知道android消息机制有一个最简单原则就是消息谁发送,谁处理?看一下封装Message的时候,我们可以没有注意到每个Message里面都有一个target对象,在发送的时候会把handler赋值给它,而在消息处理的时候,我们是这样的进行回调:msg.target.dispatchMessage(msg);用消息里面刚传进来的target对象进行回调,也就是当初的发送者,也就是后来的执行者。
因此:我们要记住二点:主进程里面只能有一个Looper和MessageQueue。
可以有多个Handler。
在实际应用我们也可以自己创建 属于自己的Looper和MessageQueue, Handle。
首先,我们创建一个线程然后在run检测MessageQueue
public class LooperThread extends Thread{
private Looper mMainLooper;
public LooperThread() {
}
@Override
public void run() {
super.run();
Looper.prepare();
mMainLooper = Looper.myLooper();
Looper.loop();
}
public Looper getLooper() {
return mMainLooper;
}
}
然后,我们实现消息处理类的Handler:
class MyHandlerHandler extends android.os.Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//消息处理。
}
}
使用创建handler 对象的时候我们指定使用消息队列。
Handler handler = new Handler(mLooperThread.getLooper());
再后,我们就可以用handler发送消息了。