您的位置:首页 > 产品设计 > UI/UE

android的消息处理机制(图+源码分析)——Thread,Looper,MessageQueue,Message,Handler之间的关系

2014-02-28 20:17 676 查看
本文主要参考:http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html  

前言说明

先看一段非常经典的代码,我们从会这里入手,慢慢分析源码去找这五个类的相互关系,以及消息封装,消息入队,消息出队,消息处理整个过程。

public class LooperThread extends Thread {
private Handler handler1;
private Handler handler2;

@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();

// 实例化两个handler
handler1 = new Handler();
handler2 = new Handler();

// 开始循环处理消息队列
Looper.loop();
}
}


先贴一张,我理解的它们五者的关系图:



当这个Looper线程启动会依次会prepare()和looper这两个方法。

我们会以这两个方法为主线,浏览源码逐步分析他们之间的关系。

先看prepare().

private static final String TAG = "Looper";

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

/** Initialize the current thread as a 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");
}
sThreadLocal.set(new Looper(quitAllowed));
}
 从上面代码可以看到,执行prepare方法的实质,就是将Looper的成员变量sThreadLocal设置一个新的Looper对像。

 听起来有一点绕是吧,为什么不直接保存一下Looper对象呢?而ThreadLocal<Looper>这个类又是做什么的?Looper与Thread又是什么关系?

看来要把这几个问题说清楚,还是要从sThreadLocal.set(new Looper(quitAllowed)); 这个方法说起啊。

/**
* Sets the value of this variable for the current thread. If set to
* {@code null}, the value will be set to null and the underlying entry will
* still be present.
*
* @param value the new value of the variable for the caller thread.
*/
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
/**
* Gets Values instance for this thread and variable type.
*/
Values values(Thread current) {
return current.localValues;
}
/**
* Creates Values instance for this thread and variable type.
*/
Values initializeValues(Thread current) {
return current.localValues = new Values();
}
/**
* Sets entry for given ThreadLocal to given value, creating an
* entry if necessary.
*/
void put(ThreadLocal<?> key, Object value) {
cleanUp();

// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;

for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];

if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}

if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}

// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}

// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}

从上面的代码分析可以知道sThreadLocal.set(new Looper(quitAllowed)); 实质是将Thread类的localValues的成员变量设置一个新的Looper,所以也就是说一个线程只能有一个Looper。

消息出队和消息处理

再来看一下Looper的looper()方法:

/**
* 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();
}


这里有五个关键的步骤

(1)final Looper me = myLooper(); 根据当前的线程获取Looper对象。

(2)final MessageQueue queue = me.mQueue;根据当前的Looper获取它MessageQueue类型的成员变量

(3)Message msg = queue.next();出队例。

(4)msg.target.dispatchMessage(msg); 将msg传递给目标Handler,再出所在的Handler进行处理。

(5)msg.recycle(); 消息回收

 再来仔细看一下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);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
 }
 看来在Handler眼中,消息有两种处理方式。

(1) 先看Message的Runnable类型的callback成员变量是否为空,如果不为空。 则直接调用callback的run方法。

请注意这里调用的是run方法哦,而不是我们经常用的start方法。所有说这里虽然是使用了Runnable,但是我们没有把它作为另外一个线程来处理。run方法里的执行仍然和Handler处在同一个线程。

(2)先是尝试通过Callback回调的方法处理消息。如果处理成功(即Callback的handleMessage方法返回true),则直接返回。如果处理失败可在由重写Handler的handleMessage方法来处理(亲,虽然Callback和Handler处理消息的方法都叫handleMessage,但是Handler里那个没有返回布尔值哦)。

以上讲的是消息MessageQueue出取出,在由Handler处理的过程。

消息封装和消息入队

下面要说的是怎样把消息放入MessageQueue里。我们经常会这样写:

(1)先是obtain一下

/**
* Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
* @param h  Handler to assign to the returned Message object's <em>target</em> member.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;

return m;
(2)再sendToTarget一下

/**
* 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);
}


通过以上两步,消息就成功的进入到对应的MessageQueue里啦。

/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @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.
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}/**
* 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.
*
* @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 final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}/**
* 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);
}

可以看到通过以上三个方法,Message就进入到了MessageQueue里了。

哦,所以我们现在重点就是MessageQueue从哪里来的?

我们在Handler的构造方法找到了如下代码:

/**
* 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 represent 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();
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;
}
所以mQueue是这样得到:

(1)根据Handler得到当前运行的Thread线程

(2)通过Thread里面的localValues成员变量找到线程里面的Looper

(3)再拿到Looper里面MessqgeQueue的引用就可以了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐