本篇文章浅析android Handler、Looper、Message等之间的交互以及特别要注意的地方,在此做一记录,以备后续。
接下来,分别简要的介绍一下Handler、Looper、Message。
一、概要
1.Handler(简要的说:a.分发消息;b.异步处理)
There are two main uses for a Handler:
(1) to schedule messages and runnables to be executed as some point in the future;
(2)to enqueue an action to be performed on a different thread than your own.
1)构造
具体的构造方法,再者就不阐述了,下面贴一段主要的构造源码:
public Handler(Callback callback, boolean async) {
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;
}
接下里,看看Handler有哪些成员:
final MessageQueue mQueue ;
final Looper mLooper;
final Callback mCallback ;
final boolean mAsynchronous;
对比上文
首先,我们可以看到,在构造Handler的时候,我们可以在外部传进Callback回调处理,这块涉及到Handler的消息处理回调,后续会讲:
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
其次, 该Handler 分发的消息都是异步的Asychronous(这个是在Handler enque 一个message的时候判断设置的),这个将来在MessageQueue在Looper循环去除Message的时候会做一判断;
if (mAsynchronous) {
msg.setAsynchronous( true);
}
此外,我们还看到有一个mLooper成员变量,其实查看源码,我们可以看到,Handler提供一个构造方法,可以让传进外边的Looper;
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
而前面的那个构造方法,我们可以看到其有调Looper.myLooper(),来关联一个Looper。
最后,我们还看到有一个mQueue成员变量,其类型是MessageQueue,而其赋值从上面的两个构造方法中也可以看出,是通过将Looper的mQueue赋值给它
mQueue = looper.mQueue;
同上述对Handler构造的简要描述,我们可以看到,Handler具有消息分发功能(持有MessageQueue,实际是Looper的)以及消息处理功能(持有Looper、mCallBack)的硬件条件。
2)消息处理
1.消息发送:
a.sendXXX系列:
send系列最终都会调到一个方法sendMessageAtTime;
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var> .
* <b>The time -base is {@link android.os.SystemClock#uptimeMillis}. </b>
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time -base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
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中;
b.postXXX系列:
post系列最终也是通过调用sendMessageAtTime方法,将消息加入到MessageQueue中的,
先看一个post系列的方法的声明:
public final boolean post(Runnable r);
我们看到其参数是一个Runnable,而sendMessageAtTime需要将一个Message加入到Queue中,因此,就需要个方法将Runnable转换成Message,
这个方法就是:
private static Message getPostMessage (Runnable r) {
Message m = Message. obtain();
m.callback = r;
return m;
}
*这里记住一点就是:m.callback = r;m是Message(这里与Handler处理消息有关)
c.Message.sendToTarget系列:
Message有个成员变量target,其类型为Handler;
简单的来说,一个Message会关联分发其的Handler(通过Handler创建Message的时候),如当Handler调用obtain系列来创建一个Message的时候会关联Handler
public static Message obtain(Handler h) {
Message m = obtain();
m. target = h;
return m;
}
其次,再调用Message的sendToTarget,通过Handler分发消息。
/**
* Sends this Message to the Handler specified by {@link #getTarget}.
* Throws a null pointer exception if this field has not been set.
*/
public void sendToTarget() {
target.sendMessage (this );//target的类型就是Handler
}
2.消息处理(回调):
这里主要涉及Handler的消息处理回调,即,Looper通过在loop()方法中调用
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);
}
}
如果Message具有callBack的话执行handlerCallback,是不是依稀记得上文在调用Handler的post发送消息的时候,将一个Runnable复制给Message的callBack,
下来,我们先看看handlerCallback这个方法:
private static void handleCallback (Message message) {
message.callback.run();
}
果然如此,不用多说了
接下里,如果Message 没有callback ,调用Handler自己的监听回调mCallBack的handleMessage方法,callBack是CallBack类型,其实通过在构造方法中传递进来的。
最后就是通过调用handleMessage方法来处理了,其是一个空方法,如果要处理,需要在Handler的子类中进行复写。
2.Looper
1)成员变量:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
2)相关成员变量解释
我们看到:
sThreadLocal 保存线程相关变量,主要是给当前线程绑定Looper;
sMainLooper: 主要是用于prepareMainLooper来进行设置;
具体的应用,在android UI线程中设置,如ActivityThread中的main()方法:
public static void main(String[] args) {
,,,,//此处有很多代码
Looper.prepareMainLooper();
,,,,//此处有很多代码
Looper.loop();
}
注意一点就是,prepareMainLooper,不一定就是将此Looper与UI线程关联,其调用如下:
public static void prepareMainLooper () {
prepare( false);
synchronized (Looper. class) {
if ( sMainLooper != null) {
throw new IllegalStateException( "The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
我们可以看到,其最终还是调用myLooper来获取当前线程绑定的Looper对象,当然再次之前须调用一次prepare方法,注意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));
}
再跟构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread. currentThread();
}
quitAllowed:消息处理是否允许退出。
当我们调用Looper的quit方法,就会调到Queue的quit方法。
可以看一下MessgaeQueue的quit方法
final void quit() {
if (! mQuitAllowed) {
throw new RuntimeException( "Main thread not allowed to quit.");
}
synchronized ( this) {
if ( mQuiting) {
return;
}
mQuiting = true;
}
nativeWake( mPtr);
}
我们可以看到,通过该标识,来判断当前线程是否退出,注意:主线程不允许quit哦
3)消息处理分发:
谈到Looper,不得不谈的一点就是Looper的loop函数,其是实现消息分发的核心之处:
首先,其拥有一个while true循环(此Looper循环是有VM来控制的,Looper并没有另开启一个线程来专门做这个事情)
其次,从MessgaeQueue中取一个Message;
最后,调用Handler的dispatch..方法将消息交由Handler处理;
/**
* 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;
,,,
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
,,,//此处有很多代码
msg.target.dispatchMessage(msg);
,,,//此处有很多代码
msg.recycle();
}
}
从loop中我们可以看出,首先当前线程的Looper是不是存在,然后从MessageQueue中获取一个Message交由Handler处理;
最后将该Messgae回收。
在上面的loop中我们注意到,获取Message的时候,我们调用Queue的next方法,我们接下来,看看这个next方法:
final Message next() {
,,,//此处有很多代码
for (;;) {
synchronized ( this) {
if ( mQuiting) {
return null;
}
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg. target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg. next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg. when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = ( int) Math.min(msg. when - now, Integer. MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg. next = msg.next ;
} else {
mMessages = msg.next ;
}
msg. next = null;
if ( false) Log. v( "MessageQueue", "Returning message: " + msg);
msg. markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1 ;
}
,,,//此处有很多代码
}
,,,//此处有很多代码
}
}
在说这段代码之前,首先要说一下想MessageQueue中加入消息;
final 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.");
}
boolean needWake;
synchronized ( this) {
,,,
msg. when = when;
Message p = mMessages;
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;
}
}
if (needWake) {
nativeWake( mPtr);
}
return true;
}
从上文我们可以看到,当将一个Messgae加入到Queue,是按时间的先后顺序来加入的(当然前提的是消息是同步的)
查看enqueueMessage,我们发现mMessages保存第一个加入到Queue中的有效的Message(何为有效,三点:1.target不为null;2.时可用的Message;3.发送消息的时间至少要小于一个现已存在Queue中Message)
好,明确Queue中的mMessages是保存第一个加入到Queue中的有效的Message之后,我们在回过头来看一下Queue的next()方法 经过一系列的过滤,调用
Message prevMsg = null;
Message msg = mMessages;
if (prevMsg != null) {
prevMsg. next = msg.next ;
} else {
mMessages = msg.next ;
}
msg. next = null;
if ( false) Log. v( "MessageQueue", "Returning message: " + msg);
msg. markInUse();
取出第一个有效的Message。
3.Message
对于Message,主要说一点,即调用什么方法创建Message。
首先,Message有其构造方法,也就是说 ,我们可以通过new Message()来创建一个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();
}
此方法采用了缓冲,如上所说避免了创建新的对象,以节省资源。此方法与recycle方法互补调用;
/**
* Return a Message instance to the global pool. You MUST NOT touch
* the Message after calling this function -- it has effectively been
* freed.
*/
public void recycle () {
clearForRecycle();
synchronized ( sPoolSync) {
if ( sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
recycle方法实在Looper的loop处理函数中,当将一个消息处理完之后,就会调用其recycle,以便后续重用。
二、举例说明
1.Handler的创建
1)在UI线程中创建;
private void createHandlerInUIThread() {
//调用无参构造方法,此时会在其构造方法中调用Looper.myLooper(),而主线程中已调用Looper.prepare(true),故可获取Looper
Handler mainHandler = new Handler();
}
Log:04-10 17:29:12.499: I/com.handler.test.HandlerActivity(4715): ———–>createHandlerInUIThread looper:Looper{41766448}
这个运行是可以的
2)在子线程中创建;
private void createHandlerInSubThread() {
new Thread( new Runnable() {
@Override
public void run() {
//调用无参构造方法,此时会在其构造方法中调用Looper.myLooper(),而此时子线程中没调用Looper.prepare(),故可不能获取Looper,会报错
Handler mainHandler = new Handler();
Log. i( TAG, "----------->createHandlerInSubThread looper:"+Looper.myLooper ());
}
}).start();
}
Log:04-10 17:31:31.019: E/AndroidRuntime(4811): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
如果要解决这个问题,只需如在子线程创建Handler之前,调用Looper的prepare方法即可;
private void createHandlerInSubThread() {
new Thread( new Runnable() {
@Override
public void run() {
//调用Looper的 oprepare方法
Looper. prepare();
Handler mainHandler = new Handler();
Log. i( TAG, "----------->createHandlerInSubThread looper:"+Looper.myLooper ());
Looper. loop(); //如果步调用这个方法,则不会处理消息
//如果把log放在这里是打印不出来的,因为loop是一个死循环,不会执行到下面的语句,也进一步说明loop并没有开启另一个线程来执行循环
//Log. i( TAG, "----------->createHandlerInSubThread looper:"+Looper.myLooper ());
}
}).start();
}
Log:04-10 17:38:01.379: I/com.handler.test.HandlerActivity(5098): ----------->createHandlerInSubThread looper:Looper{417dd680}
1.在主线程中操作主线程的Handler改变UI
protected void interactiveMainHandlerAndSubLooper() {
Handler mainHandler = new Handler(loopers .get(“sub” ));//持有子线程的looper
Log. i( TAG, “———–>interactiveMainHandlerAndSubLooper looper:”+mainHandler.getLooper());
}
Log:
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): —————>onitemClick:view=16908308;position=0;id=0
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): ———–>createHandlerInUIThread looper:Looper{41762d40}
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): ———–>createHandlerInSubThread looper:Looper{417d6ae8}
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): —————>onitemClick:view=16908308;position=1;id=1
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): ———–>interactiveMainHandlerAndSubLooper looper:Looper{417d6ae8}
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): ———–>interacticeSubHandlerAndMainLooper looper:Looper{41762d40}
2.在主线程中操作子线程中拥有Looper的Handler来操作UI
protected void interacticeSubHandlerAndMainLooper() {
new Thread( new Runnable() {
@Override
public void run() {
Handler subHandler = new Handler(loopers .get("main" ));//持有子线程的looper
Log. i( TAG, "----------->interacticeSubHandlerAndMainLooper looper:"+subHandler.getLooper());
}
}).start();
}
Log:
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): —————>onitemClick:view=16908308;position=0;id=0
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): ———–>createHandlerInUIThread looper:Looper{41762d40}
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): ———–>createHandlerInSubThread looper:Looper{417d6ae8}
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): —————>onitemClick:view=16908308;position=1;id=1
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): ———–>interactiveMainHandlerAndSubLooper looper:Looper{417d6ae8}
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): ———–>interacticeSubHandlerAndMainLooper looper:Looper{41762d40}
3.在主线程中操作主线程的Handler改变UI
这个请自行脑补;
4.在主线程中操作子线程中拥有Looper的Handler来操作UI
可以操作UI,但除了setContentView();否则会报非UI线程的错误;
Log: 04-10 17:58:57.879: E/AndroidRuntime(5617): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
5.在子线程中通过操作UI线程的Handler来改变UI
这个也很常用,请自行脑部;
6.在子线程中通过操作拥有Looper的子线程的Handler来操作UI;
可以操作UI,但除了setContentView();否则会报非UI线程的错误;
Log:04-10 17:58:57.879: E/AndroidRuntime(5617): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
另外,我在网上看到几篇写的不错的文章,将链接附上,有些图我就再者不画了:
1.http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html
2.http://android-developers.blogspot.hk/2007/11/stitch-in-time.html
3.http://www.apkbus.com/home.php?mod=space&do=blog&uid=623357&id=58615