Looper的准备阶段:
无论是在UI线程还是在子线程,想要使用Handler就一定要先做一个准备的操作,就是创建一个Looper和MessageQueue。
1.在UI线程方面的准备工作:
在ActivityThread.java里面,程序的入口main( )里面已经调用了Looper.prepareMainLooper();
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
在Looper类中propareMainLooper( )方法做了哪些事情?
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(boolean )方法里面是这样的:
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));
}
这里最后一行代码出现了一个ThreadLocal变量sThreadLocal,这个变量的作用是在每个线程保存其要使用到的变量。
再来看Looper的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper的构造方法里面创建了一个MessageQueue和与当前Looper相对应的Thread,并将Looper的mQueue属性设置为刚new出来的MessageQueue,将Looper的mThread属性设置为当前的Thread,在程序的入口main( )里面,此时的Thread则为UI线程,所以在这里UI线程和Looper、MessageQueue已经产生关联,就是在一进入程序的时候就已经为UI线程创建了与之相对应的Looper和MessageQueue了,这是在UI线程里面的情况。
2. 在子线程方面的准备工作:
要想在子线程中使用Handler则先要做一些准备工作,这些准备工作就是创建一个Looper和MessageQueue,在子线程使用Handler时不像在UI线程中使用Handler那样,因为UI线程在程序入口的时候就已经创建了一个Looper和MessageQueue了,如果在子线程中直接使用Handler则会报"Can't create handler inside thread that has not called Looper.prepare()"这样的异常,所以子线程要想使用Handler那也要像UI线程那样先创建一个Looper和MessageQueue。根据刚才的报错提示可以找到Looper类里面的prepare()方法。在new Handler( )之前就先调用Looper.prepare( )方法来创建一个与此子线程绑定的Looper和MessageQueue(这上步在下面的小节中有说明)。具体代码如下:
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");
}
sThreadLocal.set(new Looper(quitAllowed));
}
可以看到最后会调用到prepare(boolean) 调用方法,那这就跟UI线程创建Looper和MessageQueue还有绑定当前Thread一样了,只不过当前的Thread是个子线程。
小结:
无论是在UI线程的层面还是子线程层面上使用Handler都是需要准备一个Looper、MessageQueue还有将当前线程与Looper、MessageQueue进行绑定的,只不过UI线程的这些准备动作在程序入口main()那里完成的,一般情况下我们看不到这些。
Looper不断地轮循着MessageQueue:
经过了前面一步的准备工作之后,现在已经创建了一个Looper和与之相对应的MessageQueue了,前面在UI线程方面的准备工作中,看到main( )里面还有一个语句就是Looper.loop();这是在UI线程下的Looper与MessageQueue都创建好之后才调用的,这语句是让Looper开始轮循MessageQueue。
1.先让Looper无限地轮循起来:
以下是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
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.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();
}
}
看到第一个语句是final Looper me = myLooper();
看看myLooper()方法里面又有些什么?
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到这个方法是从当前Thread下的ThreadLocal容器中取出属于此线程的Looper。如果me为空,则会抛出异常,异常会提示说没有调用到Looper.prepare( )方法,因为如果没调用Looper.prepare( )方法,就不会去创建Looper实例,那么也就没有MessageQueue来装载Message了,所以会抛出异常。接着下一个语句:final MessageQueue queue = me.mQueue;这是从上一步中取出Looper之后,再通过Looper拿到与之相应的MessageQueue。之后会进入一个for( )循环中,此时的MessageQueue也是在不停地循环取出其内部的Message。请看接下的第2小节。
2.在loop( )中的for( )不停地调用MessageQueue.next( ):
进入到MessageQueue.next( )里面,发现很多东西都涉及到Native层,这里不作解析,只知道这个方法的主要作用是在MessageQueue中不断地将Message取出来。最后到try语句里面msg.target.dispatchMessage(msg);
这时的msg的target属性就是与之对应的Handler,最后调用Handler.dispatchMessage(msg)方法。
小结:
这里先取出当前线程所对应的Looper,然后取出该Looper里面的属性mQueue,这样就有MessageQueue可用了,最后调用MessageQueue.next( )方法将Message取出,取出的Message内有target属性,Looper会根据这个target属性来将Message派发给相应的Handler来处理Message。
创建Handler使用post或send方式发送Message到MessageQueue:
1. 首先new Handler()的位置是在UI线程还是在子线程:
先看Handler的默认构造方法:
public Handler() {
this(null, false);
}
接着会调用到这个构造方法:
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;
}
如果是在UI线程中new Handler( )的,那么到这一步mLooper = Looper.myLooper();之后会根据当前的UI线程找到UI线程所对应的Looper,这个Looper是在main()里面通过调用Looper. prepareMainLooper()来得到Looper的,这个Looper里面已经有一个MessageQueue,并且此Looper已经与UI线程进行了绑定,所以,在UI线程new Handler()的时候,无论发送Message是在子线程中进行的还是在UI线程中进行的,结果还是在UI线程中处理Message。如果是在子线程中new Handler( )的,那么走到mLooper = Looper.myLooper();这里会抛出异常,因为这个子线程没有Looper和MessageQueue。解决方法是先在new Handler( )的前面先调用Looper.prepare()方法来创建Looper与MessageQueue,并且将此子线程与此Looper绑定,最后再调用Looper.loop()方法进行轮循MessageQueue。
2.Looper、Thread、MessageQueue、Handler之间的联系:
在Looper准备阶段,Looper已经与Thread和MessageQueue进行了绑定,当Handler对象是在UI线程中创建的,就会得到UI线程所绑定的Looper与MessageQueue,当Looper派发Message至Handler时,会在UI线程上处理Message;当Handler对象是在子线程中创建的,要先调用Looper的prepare()和loop()方法来准备一个Looper并将此Looper与此子线程进行绑定,在它们绑定的同时还会创建一个MessageQueue,然后再将此Looper轮循着这个MessageQueue,当Looper派发Message至Handler时会在子线程上处理Message。
3.使用post方式来发送Message:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
先看看getPostMessage(r)里面有些什么?
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
这里先调用Message.obtain()来获取Message,之所以不用new的方式来创建Message,是因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。接着将post(Runnable r)方法里面的Runnable传给了Message的callback属性,之后等这个带有callback属性的message被派发到Handler时,Handler会根据这个callback属性来进行不同的处理操作。接着就到了sendMessageDelayed(getPostMessage(r), 0)这里了,
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
再看sendMessageAtTime (Message msg, long uptimeMillis):
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(MessageQueue queue, Message msg, long uptimeMillis):
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里会先设置Message msg的target属性,将其设置为当前的Handler,用于给Looper辨别是哪一个Handler发送了此条Message,最后一句调用了MessageQueue的enqueueMessage(Message msg, long when)方法来将Message发送到MessageQueue中去。
4.使用send方式来发送Message:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
再看sendMessageDelayed(Message msg, long delayMillis):
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
再看sendMessageAtTime(Message msg, long uptimeMillis):
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);
}
最后还是会和post方式一样调用到sendMessageAtTime(Message msg, long uptimeMillis)方法,最后调用到enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法将Message传入MessageQueue当中,这就相当于Message入消息队列了。
小结:
Handler对象创建在UI线程上可以不用调用Looper.prepare( )方法和Looper.loop( )方法,因为这在程序入口main( )那里已经做好了;当Handler对象创建在子线程上时,一定要在创建Handler对象前面调用Looper.prepare( )和Looper.loop( )方法,只有这样做才会有与此子线程绑定的Looper和MessageQueue,并且Looper会不断地轮循着MessageQueue。另外,Handler在发送Message给MessageQueue的时候是有两种方式的,一种是post方式,另一种是send方式。post方式boolean post(Runnable r)是要带有一个Runnable参数的,千万不要以为这里创建了一个线程,因为此时并没有将Runnable作为参数放进Thread里面,实际上这个Runnable参数只是将自己赋值给了Message的callback属性而已;send方式和post方式最后都会调用到sendMessageAtTime(Message msg, long uptimeMillis)方法然后通过enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
方法的里面的最后一句MessageQueue.enqueueMessage(Message msg, long when)来将Message根据所设置的时间参数来进行入队,时间参数越小就越早MessageQueue。
Looper会将从MessageQueue里面拿出来的Message根据其target属性向指定的Handler派发消息:
经过上一步的分析,发现Handler的两种发送Message方式最后都会调用到sendMessageAtFrontOfQueue(Message msg)和enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis),其中enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法里面:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
第一行就设置了Message的target属性了,就是将当前的Handler赋值给了Message的target属性。然后到Looper.loop()方法里面
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
有这么一个语句,这就是根据Message的target属性来调用dispatchMessage(Message)方法的,此时的target属性指的是发送该Message的Handler。
小结:
Looper之所以能精准地派发Message到Handler手里,是因为在将Message入队之前就为其标记好了相对应的Handler了,在Handler类里面的enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法中为Message设置了target属性,属性值为当前的Handler,这个就是标记,所以到Looper轮循派发Message的时候会根据这个标记来调用Handler里面的dispatchMessage(Message msg)方法。
Handler根据post或send方式来处理Message:
Looper在上一步中根据Message的target属性来派发Message至相应的Handler之后,Handler是如何处理Message的?
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
先看if语句里面,Message的callback属性不为null,则调用handleCallback(Message message)方法。这个Message的callback属性是什么时候添加的呢?还记不记得在Handler里面使用post方式来发送Message的时候
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
再看getPostMessage(Runnable r)方法内部:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
原来Message的callback属性是在使用post方式发送Message时所设置的Runnable啊!如果此时是使用Handler的post方式来发送Message,那么在Looper派发完Message之后会调用Handler的handleCallback(Message message)方法
private static void handleCallback(Message message) {
message.callback.run();
}
前面讲post方式来发送Message的时候提到post(Runnable r)方法里面的Runnable参数,这不是在创建一个线程,因为在handleCallback(Message message)方法里面是直接调用了这个Runnable的run()方法,这是不会创建线程或启动线程的,所以有时候为了方便是会这样写post方式发送Message的:
handler_post.post(new Runnable() {
@Override
public void run() {
Log.d("post",Thread.currentThread().getName());
tv_show.setText("RUOK?");
}
});
如果使用send方式发送Message,则会走dispatchMessage(Message msg)
的else里面,
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
这里面还有一个Handler的mCallback属性,如果new Handler()的时候选用Handler的默认构造方法,则此mCallback属性则为null,所以最后处理Message的时候会走到这一句handleMessage(msg);再走进发现public void handleMessage(Message msg) {}是一个空方法,这就是我们通常使用send方式来发送Message,最后还要重写handdleMessage(Message msg)方法的原因了,以下是handleMessage(Message msg)的最基本用法:
Handler handler_send = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.d(TAG,Thread.currentThread().getName());
String show =(String) msg.obj;
tv_show.setText(show);
}
};
小结:
如果先前使用post方式来发送Message,则在处理Message的时候可以直接在post方式的Runnable参数那里直接更改UI,如果先前使用send方式来改善Message,则在创建Handler对象的时候要重写HandlerMessage(Message msg)方法来更新UI。