1、android的消息机制概述
1.1 消息机制概述
android的消息机制是通过Handler的运行机制来实现的,handler需配合底层的Looper和MessageQueue,来完成消息的发送、分发与处理等工作流程。一般使用Handler的消息机制来完成线程间通信。
大抵流程总结如下:
子线程通过handler发送Message,并将消息插入消息队列,Looper轮循器在检测到有新消息后,Looper轮循器被唤醒并开始遍历MessageQueue消息队列,轮询取出消息并调用handler来分发消息,最终通过handler的消息分发机制来完成消息的分发,并处理相关的消息。
1.2 几个名词的意义
Looper 消息轮循,轮循取出消息,并交由handler分发
MessageQueue 消息队列,用来管理消息,底层由链表实现
Message 消息对象
Handler 消息发送、分发、处理
1.3 Message的获取与发送的几种方法
发送消息
1、handler.sendMessage(msg);
2、msg.setTarget(target); //设置发送给哪个Handler
msg.sendToTarget();
获取消息
1、handler.obtainMessage(); //obtainMessage()方法实际调用了Message.obtain方法
2、Message msg = Message.obtain(handler);// obtain(handler)调用了Message.obtain()方法
或
Message msg = Message.obtain();
msg.setTarget(target)3、Message message = new Message();
Message.obtain()方法如下,消息池有消息则直接返回消息,否则创建一个消息返回
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
* 消息池有消息则直接返回消息
* 否则创建一个消息返回
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool; // 取出消息池头部消息对象
sPool = m.next; // 将消息池头指向后一位
m.next = null; // 将取出的消息与消息池断开连接关系
sPoolSize--; // 消息池大小减一
return m;
}
}
return new Message();
}
1.4 handler机制的运行大致流程图
注意:为了让图示更清楚,所以将Looper与MessageQueue单独出来,实际上Looper是运行于Handler所在线程(通常情况下为主线程)。
2、 Handler消息机制运行流程
2.1 Looper初始化与开启轮循
ActivityThread(UI线程)的main方法中初始化Looper ,并开启消息轮循
看ActivityThread的main方法:
public static void main(String[] args) {
SamplingProfilerIntegration.start();
...
// 初始化Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
// 开启轮循
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper初始化
- 1、 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();
}
}
- 2、prepare(boolean quitAllowed),创建Looper对象,并将它保存在ThreadLocal对象中。参数quitAllowed表示是否允许可以退出消息轮循,UI线程默认不允许退出消息循环(通过prepare(false);可以看出)。
注意:一个线程只能创建一个Looper对象。
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));
}
- 3、Looper的构造方法,初始化了内部的MessageQueue,并将quitAllowed传参给它
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); // 确定Queue可不可以退出
mThread = Thread.currentThread();
}
- 4、 myLooper() , 从sThreadLocal中获取Looper对象
public static Looper myLooper() {
return sThreadLocal.get();
}
Looper开启轮循
loop() 方法开启轮循,等待处理消息
- 1、通过queue.next()取出消息
- 2、通过queue.enqueueMessage向消息队列插入一条消息
loop方法作了如下几件事:
1.取出消息,若无消息则阻塞,queue.next(); // might block
2.分发消息 ,通过msg.target.dispatchMessage(msg);来完成,实际上调用了目标handler的dispatchMessage方法
3.回收消息 ,通过 msg.recycle();来完成
4.特殊情况退出轮循
可以看到有这样一段,判断msg是否为null,正常情况不会为null,只有当我们调用了Looper的quit方法时才会使queue.next()取出的消息为null(内部调用了消息队列的quit方法, mQueue.quit(bool);),同时要注意,只有当quitAllowed为true时才能退出轮循(quitAllowed上面提到),UI线程默认不允许退出消息轮循
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
loop方法如下
/**
* 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.recycle();
}
}
2.2 Handler的构造方法
handler对象必须在有Looper的线程中创建(除非我们手动创建Looper对象)
做了如下几件事:
- 1、保存mLooper对象引用,
- 2、初始化mQueue对象,用于为添加Message做准备
- 3、初始化mCallback回调
- 4、设置消息的同异步
Handler构造方法如下:
/**
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Callback callback, boolean async) {
...
// 获取Looper对象
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;
}
2.3 消息的发送与插入消息队列
- 1、发送消息的几种方法最终都会调用sendMessageDelayed方法
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
- 2、 sendMessageDelayed方法又调用了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);
}
- 3、在sendMessageAtTime中调用enqueueMessage方法插入消息
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 设置要发送给那个handler对象,设置当前的handler对象为发送的目标
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 插入消息
return queue.enqueueMessage(msg, uptimeMillis);
}
- 4、在enqueueMessage方法中通过MessageQueue的enqueueMessage向消息队列插入消息,从下面可知,消息队列是使用链表来实现的。
boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
synchronized (this) {
if (mQuitting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
2.4 handler消息分发机制
Looper通过loop方法取出消息,并交由Handler来分发消息,即msg.target.dispatchMessage(msg);其中msg.target即Handler对象
/**
* 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、首先会判断msg.callback是否为空,msg.callback即是handler.post的那个Runnable对象,不为空则调用 handleCallback(msg);
注意:handler.post()并没有开启一个新线程,Runnable的run方法运行于Handler所在线程 ,因此主线程的handler的post方法中不推荐做耗时操作
private static void handleCallback(Message message) {
message.callback.run();
}
- 2、当msg.callback为空时,要判断mCallback是否为空,mCallback是一个回调接口
public interface Callback {
public boolean handleMessage(Message msg);
}
Handler有如下构造
public Handler(Callback callback) {
this(callback, false);
}
可以用来构造Handler,如下
Handler h=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// TODO Auto-generated method stub
return false;
}
});
3、mCallback不为空时,则调用mCallback.handleMessage(msg),即调用了Callback中的handleMessage方法,如下handleMessage1方法
4、mCallback为空时,则调用了handler的handleMessage方法,即调用了handleMessage2方法
Handler h = new Handler(new Handler.Callback() {
// handleMessage1
@Override
public boolean handleMessage(Message msg) {
// TODO Auto-generated method stub
return false;
}
}) {
// handleMessage2
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
}
};
3、runOnUiThread原理
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
可以发现,当是UI线程的时候,直接调用Runnable 的run方法,它运行于主线程。
当不为主线程时,需要通过handler的post方法发送消息到主线程消息队列,如下流程 :
1 通过 mHandler.post方法发送一个消息
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
2 通过getPostMessage方法初始化一个消息对象,并给它的callback赋值
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
3 Handler在dispatchMessage中处理消息,此时msg.callback不为空,msg.callback即是handler.post的那个Runnable对象,此时handleCallback(msg);会被调用。最终导致Runnable的run方法被调用。
View的post方法原理类似。
4、几个思考
1、handler.post(Runnable r)有开启一个新线程吗?
追踪源码发现,最终是通过 message.callback.run();来开启任务的,并没有开启一个新线程,Runnable的run方法运行于Handler所在线程
2、子线程是如何切换到主线程的?
通过Handler来切换线程。在子线程中将消息发送到MessageQueue,在Looper轮循消息时,调用了msg.target.dispatchMessage(msg);让Handler来分发处理消息,此时已从子线程切换到主线程了。
3、Looper的loop方法为什么不会卡死主线程?
loop方法会阻塞主线程但不会卡死主线程。在源码中我们知道,在 Looper.loop();之后又这么一句
throw new RuntimeException(“Main thread loop unexpectedly exited”);
我们可以知道如果loop结束运行则会抛出此异常。因此loop方法正常是不会结束的。它配合ActivityThread内部Handler(即mH),通过不停的消息轮循与事件处理来完成与用户的交互。
4、为什么在子线程中创建Handler时会出错?
由上面我们分析源码可知,在构造Handler时会初始化Looper对象,而子线程通常是没有Looper对象的(如果我们并没有自己创建Looper对象的话),会抛出如下异常
09-07 19:55:44.050: E/AndroidRuntime(1870): java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
为了解决这个错误,我们可以手动创建Looper对象,并开启消息轮循(以下仅仅简单示例,可以做更多拓展)
new Thread(){
public void run() {
Looper.prepare();
Handler h=new Handler(){
public void handleMessage(android.os.Message msg) {};
};
Looper.loop();
};
}.start();
这样就解决了问题了,但同时要注意,当我们不需要使用此子线程的Handler时,我们应该手动停止Looper以回收资源,通过Looper的quit(立即停止)或quitSafely(当消息轮循完时)即可停止消息轮循。