Android消息机制,对每个开发来说,都不可避免的和它打交道,比如在子线程中更新UI、倒计时功能等等都需要使用到消息。
从开发的角度来讲,Handler是Android消息机制的上层接口,我们只需要和Handler打交道就足够了。
Handler的使用:
// 处理消息
Handler handler = new Handler(){
@overider
public void handleMessage(Message msg) {
// do somethings you want to do!
super.handleMessage(msg);
}
}
// 发送消息
Message msg = Message.obtain();
msg.seData(bundle);
msg.setTarget(mHandler);
msg.sendToTarget();
// 或者使用mHandler.sendMessage(msg);
Handler的使用很简单,我们就不在此举例赘述了。但是想更娴熟的使用Handler,驾驭消息机制,我们还需要从源码层面来讲整个机制的运作,读懂Handler和Looper、MessageQueue、Message的关系。
请看下图:
下面我们从源码一步步的分析消息运行机制:
一、Handler:
1.1Handler的构造方法
handler的构造方法如下:
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
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;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从Handler的构造方法,我们可能看出,Handler里面保存了Looper、MessageQueue的引用。
1.2消息的发送
发送消息有两种方式:
- 1、msg.sendToTarget();
- 2、mHandler.sendMessage(msg);
先看1方式:
public void sendToTarget() {
target.sendMessage(this);
}
最终还是调用的handler的sendMessage方法。
再看2方式都干了什么事情。
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//将要发送的Message绑上该Handler的标签
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//将消息将入Handler构造函数中初始化的MessageQueue队列
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到sendMessage就是做了一个入队列的操作(即enqueueMessage)
消息发送完成 ,接下来我们看看消息是如何取出来的,再看这个之前,我们先看看Looper的相关设置:
二、Looper:
Looper,在消息机制中扮演者消息处理角色,即从MessageQueue中不断的取消息,然后分配给对应的Handler去handleMessage。
2.1 Looper的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
这是一个私有的构造方法。我们可看到,在构造方法中,每个Looper都和一个MessageQueue绑定。
Looper里面的主要引用如下:
- static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
- private static Looper sMainLooper; // guarded by Looper.class
- final MessageQueue mQueue;
- final Thread mThread;
这几个成员变量比较好理解,MessageQueue是Looper操作的消息队列,mThread是当前线程的对象,sMainLooper是主线程的Looper对象引用。
sThreadLocal 这个对象比较特殊,主要作用将Looper和线程关联,这样外部就可以直接通过Looper拿到当前线程的Looper对象了,这样可以保证Handler创建的时候与当前线程的Looper关联。
关于 ThreadLocal<T>的简介和用法可以看这篇文章Android 多线程系列----ThreadLocal的使用和原理
2.2 Looper的获取
Looper没有public的构造方法,只能使用Looper.myLooper来获取实例,在获取对象前需要调用Looper.prepare()方法。(注:在UI线程,ActivityThread已经帮忙做了这些事情了,所以我们创建Handler不需要操作Looper)
下面看Looper的重要方法的源码:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//Looper类会创建一个新的Looper对象,并放入全局的sThreadLocal中。
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static Looper myLooper() {
return sThreadLocal.get();
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
通过上面代码,我们可以得到以下结论:
- 1、每个线程中,只能有一个Looper,因为多次调用Prepare会报异常
- 2、每个线程也只要一个MessageQueue,一个线程中,所有的消息公用一个队里
- 3、Looper.myLooper提供给外部实例,通过ThreadLocal实现的
2.3Looper的loop,获取消息
消息循环的启动,需要调用Looper的loop方法。
这个方法的作用就是不断的从MessageQueue中获取消息,然后处理,我们先看看源码:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
//获取当前线程的Looper对象实例
final Looper me = myLooper();
//调用loop()方法之前必须在当前线程通过Looper.prepare()方法创建Looper对象实例。
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取Looper实例的中的消息队列
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
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));
}
//将消息分发给注册它的handler
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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();
}
}
通过上面方法,我们看到,loop方法启动了一个死循环,不断通过MessageQueue的next方法(该方法是阻塞的),不断获取消息,然后通过Message的target调用dispatchMessage分发处理消息,然后回收消息对象。
三、消息的处理:
Handler的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);
}
}
可以看到消息分发处理很简单:
- 1、如果Message有callback则调用Message的Callback
- 2、否则,如果Handler创建时候设置了Callback,调用Callback的handleMessage,如果返回true结束
- 3、否则, 调用Handler的handleMessage方法,完成消息的处理。
四、消息机制的闭环实现:
消息机制实现,有一些前提,如下:
-
1、线程直接内存是共享的(不同线程可以使用Hanler发消息,加入消息队列)
-
2、每个线程只有一个Looper和MessageQueue
-
3、Handler和Looper绑定,保证了不论哪个线程,Handler只会向一个指定对了发送消息,在取消息的时候,处理也就只会在当前Looper线程了。
-
1、创建Looper和Message对象
-
2、创建Handler对象,通过Looper.loop启动消息循环
-
3、Handler在子线程发送消息,将消息加入到MessageQueue
-
4、Looper在loop方法中(主线程),取出消息并处理,完成了消息在不同线程间的切换。
消息机制的闭环可以如下总结:
消息机制的闭环.png参考资料:
1、又一年对Android消息机制(Handler&Looper)的思考
2、Android消息机制的原理剖析—闭环总结
3、Android消息机制(一):概述设计架构
网友评论