学习Android也有一段时间了,那天被问到Handler的运行机制,我竟愣是脑袋一片空白,没有回答出来,不管是忘了也好还是紧张也罢,归根到底是没有理解,所以决定再重新看看,重新理解一下,在这做个分享,里面如果有什么不对的地方希望大神可以提出来一块探讨一下。
首先思考几个问题
1、问题
(1)Handler、Looper、MessageQueue是什么,有什么关系?
(2)MessageQueue是属于谁的?是Thread,是Handler或者是Looper,还是...
(3)消息机制是怎样工作的?
(4)怎样使用Handler
2、我们从后向前来看看这几个问题
怎样使用Handler呢?如果在UI线程中,很简单,可以这样
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
m_handler = new Handler();//在主线程实例化Handler
}
运行没有问题,那如果在子线程中这样写呢?
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
m_handler = new Handler();//在主线程实例化Handler
new Thread(new Runnable() {
@Override
public void run() {
m_threadHandler = new Handler();//在子线程实例化Handler
}
}).start();
}
运行程序会崩溃,提示错误是
5701-5724/com.example.administrator.myapplication E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-9487
Process: com.example.administrator.myapplication, PID: 5701
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
说是不能在没有调用Looper.prepare()的线程内创建Handler。那就添上那句再试试,
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
m_handler = new Handler();//在主线程实例化Handler
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
m_threadHandler = new Handler();//在子线程实例化Handler
}
}).start();
}
这样,程序果然就不崩啦,那为什么会这样呢?那我们就看看Handler的构造函数源码是怎样写的吧。
/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
this(null, false);
}
.省略
.
.
/**
* Use the {@link Looper} for the current thread with the specified callback interface
* and set whether the handler should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @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) {
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();//Handle保存Looper的引用
if (mLooper == null) {//Handle 要求有且只有一个Looper
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//保存Looper的MQ
mCallback = callback;
mAsynchronous = async;
}
单看注释就已经很清楚啦,说Handler的默认与当前线程的Looper相关联,如果线程没有Looper,Handler就不能接受消息,就会抛出异常。看源码发现,如果mLooper==null,就会抛出上面的异常,那什么时候mLooper为空呢?那就看看myLooper()返回什么吧。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
如果当前线程有关联的Looper对象,则返回,否则返回null。那怎样让当前线程关联Looper呢?当然是Looper.prepare()方法啦。
/** Initialize the current thread as a looper.初始化当前线程为looper线程
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
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哦
}
sThreadLocal.set(new Looper(quitAllowed));//把Looper对象与当前线程关联起来。
}
注释中说Be sure to call {@link #loop()} after calling this method,也就是说我们上面的写法,虽然没有报错,但真正使用的话还得调用loop()方法。
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
m_threadHandler = new Handler();//在子线程实例化Handler
Looper.loop();
}
}).start();
那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);//分发消息,msg.target是个什么对象呢?
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的消息,直到消息派发完为止。那派发MQ的消息,MQ是属于谁的呢?又是怎样派发的呢?派发后有谁进行处理呢?
final MessageQueue queue = me.mQueue;// 发现是有Looper本身的MQ数据成员给的值,那看一看Looper的构造函数吧。<pre name="code" class="java">private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建自己的MQ
mThread = Thread.currentThread();//保存当前线程,即与当前线程绑定,
}
可以看出,是Looper自己创建了MQ对象,那这个MQ肯定是Looper的啦,那线程会不会有MQ呢?通过看Thread的源码发现,里面压根没有MQ数据成员,只有一个Handler数据成员,所以说不是属于Thread的。至于分发消息,是通过这句话来处理的
msg.target.dispatchMessage(msg);//那msg.target是个什么类型呢?
就是一个Handler对象,
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
public int sendingUid = -1;
/*package*/ static final int FLAG_IN_USE = 1 << 0;
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target; //一个Handler对象
/*package*/ Runnable callback;
/*package*/ Message next;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
是通过调用Handler的public的dispatchMessage()方法
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//如果message设置了callback,即runnable消息,优先处理callback
handleCallback(msg);
} else {
if (mCallback != null) {//通过在activity或其他地方实现Handler.callback接口来设置handler自身的callback
//这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法,
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//实例化Handler后重写的handlemessage方法
}
}
分发顺便把处理也出了,当然我们要重写handleMessage方法或Runnable来实现自己的处理,这样谁派发谁处理的问题就解决了。那MQ里的消息是从那里来的呢?也就是谁将消息放到MQ的,怎样放的?
当然是通过Handler的方法将消息放到MQ的,我们可以使用post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int),sendMessage(Message), sendMessageAtTime(Message, long)和 sendMessageDelayed(Message, long)这些方法向MQ发送消息,直观来说就是两种方式 post 发送Runnable对象和send 发送Message对象,其实post发送的Runnable对象最后也封装成了Message对象用send方法发送的,看源码
/**
* Causes the Runnable r to be added to the message queue. //Runnable 被添加到MQ
* The runnable will be run on the thread to which this handler is
* attached. //这个runnable将会在handler绑定的线程上运行
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
//将Runnable对象转化为Message对象
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;//runnable就是message的callback函数
return m;
}
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
* 按时间将Message追加到MQ
*/
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);//加入MQ
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//调用MQ的入队方法
}
原来向MQ中存放Message和处理都是由Handler做的。好了还剩第一个问题,那我们总结一下,通过总结来解释第一个问题。
总结:
1、一个线程中是没有MQ的,只是实现了异步的功能,加上Looper后(Looper.prepare())变为一个伪Loop线程(因为没有调用Looper.loop()这个线程是不会进行循环工作的),不过其实他只是创建了一个MQ,并和当前线程进行了绑定,但他并不能向MQ里面存消息,也不能处理MQ里面的消息,这一切都需要Handler来处理,我们可以通过Handler的post和send来发消息,通过Runnable和handleMessage()来处理消息。
2、从Thread的源码可以看出,线程里面本身没有MQ,只有一个Handler的数据成员,而在构造的时候,也没有关联,是Looper创建了MQ,将当前线程与自己进行关联,而当Handler进行创建时,将Looper与自己进行了关联,这样也就间接与线程关联起来了。Looper是线程和Handler的桥梁,Handler负责消息的收发及处理,Looper负责循环派发,线程负责异步。
3、一个线程最多只能和一个Looper关联,但一个线程里可以建立多个Handler的实例。MQ的唯一性是借助Looper在线程的唯一性实现的。
ps:UI线程为什么没有显式调用Looper.prepare(),因为在Activity中都已经做好了,可以查看源码。
本文参考了两篇博客,附一下http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html,http://www.tuicool.com/articles/JFJvu2,这两篇写的都很好,可以看一下,更好的理解。